diff --git a/README.md b/README.md index 7bb2e86..6762b98 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@ We saw this kind of showcase on [Fabulous App](http://www.thefabulous.co/) and w # Usage ```java new MaterialIntroView.Builder(this) - .enableDotAnimation(true) .enableIcon(false) .setFocusGravity(FocusGravity.CENTER) .setFocusType(Focus.MINIMUM) @@ -51,6 +50,9 @@ dependencies { .enableFadeAnimation(true) //View will appear/disappear with fade in/out animation ``` ```java +.setFadeAnimation(200) //If enableFadeAnimation(true) is called view will fade in/out with 200 ms duration +``` +```java //ie. If your button's width has MATCH_PARENT. //Focus.ALL is not a good option. You can use //Focus.MINIMUM or Focus.NORMAL. See demos below. @@ -63,6 +65,8 @@ dependencies { .setFocusGravity(FocusGravity.LEFT) .setFocusType(FocusGravity.CENTER) .setFocusType(FocusGravity.RIGHT) +.setFocusType(FocusGravity.TOP) +.setFocusType(FocusGravity.BOTTOM) ``` ```java .setTarget(myButton) //Focus on myButton @@ -95,7 +99,17 @@ dependencies { .setUsageId("intro_fab_button") //Store intro view status whether it is learnt or not ``` ```java -.enableDotAnimation(true) //Shows dot animation center of focus area +// Draws a gesture drawable at the center of focus area. The default drawable is a pulsating dot. It can be +// overriden with `setGestureDrawableResId()``. +.enableGestureDrawable(true) +``` +```java +// Shows a custom drawable at the center of focus area. E.g. it could visually represent a specific gesture. +.setGestureDrawableResId(R.drawable.ic_cursor_hand) +``` +```java +// Shows a custom animator for the gesture drawable if enabled. +.setGestureAnimatorResId(R.animator.ic_drag_from_right) ``` ```java .enableIcon(false) //Turn off helper icon, default is true @@ -136,6 +150,9 @@ public class MyShape extends Shape { .setCustomShape(MyShape shape) +// or you can use the built-in `CircleOnEdge` shape whose focus center snaps to an edge depending on the supplied `FocusGravity`: + +.setCustomShape(new CircleOnEdge(new ViewTarget(view), FocusGravity.RIGHT, 0)) ``` # Demos diff --git a/build.gradle b/build.gradle index 4872fdf..d25912d 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:1.5.0' + classpath 'com.android.tools.build:gradle:2.3.3' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -18,7 +18,7 @@ allprojects { maven { url "https://jitpack.io" } - + google() } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 122a0dc..8a14252 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-all.zip diff --git a/materialintro/build.gradle b/materialintro/build.gradle index 876f9ab..6579a3c 100644 --- a/materialintro/build.gradle +++ b/materialintro/build.gradle @@ -1,12 +1,34 @@ apply plugin: 'com.android.library' +apply plugin: 'maven-publish' + +publishing { + publications { + aarDebug(MavenPublication) { + groupId "com.github.sevar83" + artifactId "MaterialIntroView" + version "1.6.0" + artifact("$buildDir/outputs/aar/materialintro-debug.aar") + // Add javadocs and sources artifact + // Add/Exclude project dependencies via pom.withXml + } + aarRelease(MavenPublication) { + groupId "com.github.sevar83" + artifactId "MaterialIntroView" + version "1.6.0" + artifact("$buildDir/outputs/aar/materialintro-release.aar") + // Add javadocs and sources artifact + // Add/Exclude project dependencies via pom.withXml + } + } +} android { - compileSdkVersion 23 - buildToolsVersion "23.0.2" + compileSdkVersion 26 + buildToolsVersion "26.0.1" defaultConfig { minSdkVersion 14 - targetSdkVersion 23 + targetSdkVersion 26 versionCode 1 versionName "1.0" } @@ -22,6 +44,6 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' - compile 'com.android.support:appcompat-v7:23.1.1' - compile 'com.android.support:cardview-v7:23.1.0' + compile 'com.android.support:appcompat-v7:26.0.1' + compile 'com.android.support:cardview-v7:26.0.1' } diff --git a/materialintro/src/main/java/co/mobiwise/materialintro/MaterialIntroConfiguration.java b/materialintro/src/main/java/co/mobiwise/materialintro/MaterialIntroConfiguration.java index b244b03..d709edf 100644 --- a/materialintro/src/main/java/co/mobiwise/materialintro/MaterialIntroConfiguration.java +++ b/materialintro/src/main/java/co/mobiwise/materialintro/MaterialIntroConfiguration.java @@ -1,6 +1,9 @@ package co.mobiwise.materialintro; +import android.support.annotation.AnimatorRes; +import android.support.annotation.DrawableRes; + import co.mobiwise.materialintro.shape.Focus; import co.mobiwise.materialintro.shape.FocusGravity; import co.mobiwise.materialintro.utils.Constants; @@ -23,10 +26,14 @@ public class MaterialIntroConfiguration { private int colorTextViewInfo; - private boolean isDotViewEnabled; + private @DrawableRes int gestureDrawableResId; + + private @AnimatorRes int gestureAnimatorResId; private boolean isImageViewEnabled; + private @DrawableRes int iconDrawableResId; + public MaterialIntroConfiguration() { maskColor = Constants.DEFAULT_MASK_COLOR; delayMillis = Constants.DEFAULT_DELAY_MILLIS; @@ -36,8 +43,8 @@ public MaterialIntroConfiguration() { focusGravity = FocusGravity.CENTER; isFadeAnimationEnabled = false; dismissOnTouch = false; - isDotViewEnabled = false; isImageViewEnabled = true; + iconDrawableResId = Constants.DEFAULT_ICON_DRAWABLE; } public int getMaskColor() { @@ -104,15 +111,31 @@ public void setColorTextViewInfo(int colorTextViewInfo) { this.colorTextViewInfo = colorTextViewInfo; } - public boolean isDotViewEnabled() { - return isDotViewEnabled; - } - public boolean isImageViewEnabled(){ return isImageViewEnabled; } - public void setDotViewEnabled(boolean dotViewEnabled) { - isDotViewEnabled = dotViewEnabled; + public void setIconDrawableResId(int iconDrawableResId) { + this.iconDrawableResId = iconDrawableResId; + } + + public int getIconDrawableResId() { + return this.iconDrawableResId; + } + + public int getGestureDrawableResId() { + return gestureDrawableResId; + } + + public void setGestureDrawableResId(int gestureDrawableResId) { + this.gestureDrawableResId = gestureDrawableResId; + } + + public int getGestureAnimatorResId() { + return gestureAnimatorResId; + } + + public void setGestureAnimatorResId(int gestureAnimatorResId) { + this.gestureAnimatorResId = gestureAnimatorResId; } } \ No newline at end of file diff --git a/materialintro/src/main/java/co/mobiwise/materialintro/prefs/PreferencesManager.java b/materialintro/src/main/java/co/mobiwise/materialintro/prefs/PreferencesManager.java index 0838074..203e054 100644 --- a/materialintro/src/main/java/co/mobiwise/materialintro/prefs/PreferencesManager.java +++ b/materialintro/src/main/java/co/mobiwise/materialintro/prefs/PreferencesManager.java @@ -17,11 +17,13 @@ public PreferencesManager(Context context) { } public boolean isDisplayed(String id){ - return sharedPreferences.getBoolean(id, false); + return id != null && sharedPreferences.getBoolean(id, false); } public void setDisplayed(String id){ - sharedPreferences.edit().putBoolean(id,true).apply(); + if (id != null) { + sharedPreferences.edit().putBoolean(id, true).apply(); + } } public void reset(String id){ diff --git a/materialintro/src/main/java/co/mobiwise/materialintro/shape/Circle.java b/materialintro/src/main/java/co/mobiwise/materialintro/shape/Circle.java index 6620074..2eb34bc 100644 --- a/materialintro/src/main/java/co/mobiwise/materialintro/shape/Circle.java +++ b/materialintro/src/main/java/co/mobiwise/materialintro/shape/Circle.java @@ -1,7 +1,6 @@ package co.mobiwise.materialintro.shape; -import android.graphics.Canvas; -import android.graphics.Paint; +import android.graphics.Path; import android.graphics.Point; import co.mobiwise.materialintro.target.Target; @@ -22,10 +21,15 @@ public Circle(Target target, Focus focus, FocusGravity focusGravity, int padding } @Override - public void draw(Canvas canvas, Paint eraser, int padding){ - calculateRadius(padding); - circlePoint = getFocusPoint(); - canvas.drawCircle(circlePoint.x, circlePoint.y, radius, eraser); + public Path getPath(int padding) { + if (this.padding != padding || cachedPath == null) { + this.padding = padding; + Point center = getFocusPoint(); + cachedPath = new Path(); + cachedPath.addCircle((float) center.x, (float) center.y, getRadius() + padding, Path.Direction.CW); + } + + return cachedPath; } @Override @@ -34,7 +38,7 @@ public void reCalculateAll(){ circlePoint = getFocusPoint(); } - private void calculateRadius(int padding){ + protected void calculateRadius(int padding){ int side; if(focus == Focus.MINIMUM) @@ -50,7 +54,11 @@ else if(focus == Focus.ALL) radius = side + padding; } - private int getRadius(){ + protected void setRadius(int radius) { + this.radius = radius; + } + + protected int getRadius(){ return radius; } diff --git a/materialintro/src/main/java/co/mobiwise/materialintro/shape/CircleOnEdge.java b/materialintro/src/main/java/co/mobiwise/materialintro/shape/CircleOnEdge.java new file mode 100644 index 0000000..3ff4c4b --- /dev/null +++ b/materialintro/src/main/java/co/mobiwise/materialintro/shape/CircleOnEdge.java @@ -0,0 +1,48 @@ +package co.mobiwise.materialintro.shape; + +import android.graphics.Point; + +import co.mobiwise.materialintro.R; +import co.mobiwise.materialintro.target.Target; + +public class CircleOnEdge extends Circle { + + protected int radius = 0; + + public CircleOnEdge(Target target, FocusGravity focusGravity, int padding) { + this(target, Focus.MINIMUM, focusGravity, padding, 0); + } + + public CircleOnEdge(Target target, Focus focus, FocusGravity focusGravity, int padding, int radius) { + super(target, focus, focusGravity, padding); + if (radius <= 0) { + this.radius = this.target.getView().getContext().getResources().getDimensionPixelSize(R.dimen.gesture_circle_radius); + } else { + this.radius = radius; + } + calculateRadius(padding); + } + + @Override + protected Point getFocusPoint() { + if (focusGravity == FocusGravity.LEFT) { + int xLeft = target.getRect().left + this.radius / 2; + return new Point(xLeft, target.getPoint().y); + } else if (focusGravity == FocusGravity.RIGHT) { + int xRight = target.getRect().right - this.radius / 2; + return new Point(xRight, target.getPoint().y); + } else if (focusGravity == FocusGravity.TOP) { + int yTop = target.getRect().top + this.radius / 2; + return new Point(target.getPoint().x, yTop); + } else if (focusGravity == FocusGravity.BOTTOM) { + int yBottom = target.getRect().bottom - this.radius / 2; + return new Point(target.getPoint().x, yBottom); + } else + return target.getPoint(); + } + + @Override + protected void calculateRadius(int padding) { + setRadius(radius); + } +} \ No newline at end of file diff --git a/materialintro/src/main/java/co/mobiwise/materialintro/shape/FocusGravity.java b/materialintro/src/main/java/co/mobiwise/materialintro/shape/FocusGravity.java index 88af9b4..d6c3422 100644 --- a/materialintro/src/main/java/co/mobiwise/materialintro/shape/FocusGravity.java +++ b/materialintro/src/main/java/co/mobiwise/materialintro/shape/FocusGravity.java @@ -10,5 +10,5 @@ public enum FocusGravity { * ie. We may want to focus on * left of recyclerview item */ - LEFT, CENTER, RIGHT + LEFT, CENTER, RIGHT, TOP, BOTTOM } diff --git a/materialintro/src/main/java/co/mobiwise/materialintro/shape/Rect.java b/materialintro/src/main/java/co/mobiwise/materialintro/shape/Rect.java index 9854f0d..feebeea 100644 --- a/materialintro/src/main/java/co/mobiwise/materialintro/shape/Rect.java +++ b/materialintro/src/main/java/co/mobiwise/materialintro/shape/Rect.java @@ -1,7 +1,6 @@ package co.mobiwise.materialintro.shape; -import android.graphics.Canvas; -import android.graphics.Paint; +import android.graphics.Path; import android.graphics.Point; import android.graphics.RectF; @@ -30,8 +29,15 @@ public Rect(Target target, Focus focus, FocusGravity focusGravity, int padding) } @Override - public void draw(Canvas canvas, Paint eraser, int padding) { - canvas.drawRoundRect(adjustedRect, padding, padding, eraser); + public Path getPath(int padding) { + if (this.padding != padding || cachedPath == null) { + this.padding = padding; + calculateAdjustedRect(); + cachedPath = new Path(); + cachedPath.addRoundRect(adjustedRect, padding, padding, Path.Direction.CW); + } + + return cachedPath; } private void calculateAdjustedRect() { diff --git a/materialintro/src/main/java/co/mobiwise/materialintro/shape/Shape.java b/materialintro/src/main/java/co/mobiwise/materialintro/shape/Shape.java index 37c742d..90b2f0e 100644 --- a/materialintro/src/main/java/co/mobiwise/materialintro/shape/Shape.java +++ b/materialintro/src/main/java/co/mobiwise/materialintro/shape/Shape.java @@ -1,7 +1,6 @@ package co.mobiwise.materialintro.shape; -import android.graphics.Canvas; -import android.graphics.Paint; +import android.graphics.Path; import android.graphics.Point; import co.mobiwise.materialintro.target.Target; @@ -20,6 +19,8 @@ public abstract class Shape { protected int padding; + protected Path cachedPath; + public Shape(Target target) { this(target, Focus.MINIMUM); } @@ -35,7 +36,7 @@ public Shape(Target target, Focus focus, FocusGravity focusGravity, int padding) this.padding = padding; } - public abstract void draw(Canvas canvas, Paint eraser, int padding); + public abstract Path getPath(int padding); protected Point getFocusPoint(){ if(focusGravity == FocusGravity.LEFT){ @@ -46,8 +47,15 @@ else if(focusGravity == FocusGravity.RIGHT){ int xRight = target.getPoint().x + (target.getRect().right - target.getPoint().x) / 2; return new Point(xRight, target.getPoint().y); } - else - return target.getPoint(); + else if(focusGravity == FocusGravity.TOP){ + int yTop = target.getRect().top + (target.getPoint().y - target.getRect().bottom) / 2; + return new Point(target.getPoint().x, yTop); + } + else if(focusGravity == FocusGravity.BOTTOM){ + int yBottom = target.getPoint().y + (target.getRect().bottom - target.getPoint().y) / 2; + return new Point(target.getPoint().x, yBottom); + } + return target.getPoint(); } public abstract void reCalculateAll(); diff --git a/materialintro/src/main/java/co/mobiwise/materialintro/utils/Constants.java b/materialintro/src/main/java/co/mobiwise/materialintro/utils/Constants.java index c97b6a4..7aedff5 100644 --- a/materialintro/src/main/java/co/mobiwise/materialintro/utils/Constants.java +++ b/materialintro/src/main/java/co/mobiwise/materialintro/utils/Constants.java @@ -1,5 +1,7 @@ package co.mobiwise.materialintro.utils; +import co.mobiwise.materialintro.R; + /** * Created by mertsimsek on 24/01/16. */ @@ -17,4 +19,9 @@ public class Constants { public static int DEFAULT_DOT_SIZE = 55; + public static final int DEFAULT_GESTURE_DRAWABLE = R.drawable.ic_dot; + + public static final int DEFAULT_GESTURE_ANIMATOR = R.animator.pulsate; + + public static final int DEFAULT_ICON_DRAWABLE = R.drawable.icon_question; } diff --git a/materialintro/src/main/java/co/mobiwise/materialintro/view/MaterialIntroView.java b/materialintro/src/main/java/co/mobiwise/materialintro/view/MaterialIntroView.java index 2f5afe2..a48ac8c 100644 --- a/materialintro/src/main/java/co/mobiwise/materialintro/view/MaterialIntroView.java +++ b/materialintro/src/main/java/co/mobiwise/materialintro/view/MaterialIntroView.java @@ -1,16 +1,16 @@ package co.mobiwise.materialintro.view; +import android.animation.Animator; +import android.animation.AnimatorInflater; import android.annotation.TargetApi; import android.app.Activity; import android.content.Context; -import android.graphics.Bitmap; import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffXfermode; +import android.graphics.Path; import android.os.Build; import android.os.Handler; +import android.support.annotation.AnimatorRes; +import android.support.annotation.DrawableRes; import android.util.AttributeSet; import android.util.TypedValue; import android.view.Gravity; @@ -20,6 +20,7 @@ import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.widget.ImageView; +import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; @@ -96,10 +97,7 @@ public class MaterialIntroView extends RelativeLayout { */ private Target targetView; - /** - * Eraser - */ - private Paint eraser; + private Path targetShapePath; /** * Handler will be used to @@ -107,14 +105,6 @@ public class MaterialIntroView extends RelativeLayout { */ private Handler handler; - /** - * All views will be drawn to - * this bitmap and canvas then - * bitmap will be drawn to canvas - */ - private Bitmap bitmap; - private Canvas canvas; - /** * Circle padding */ @@ -153,22 +143,26 @@ public class MaterialIntroView extends RelativeLayout { private boolean isInfoEnabled; /** - * Dot view will appear center of + * Gesture view will appear center of * cleared target area */ - private View dotView; + private ImageView gestureView; - /** - * Dot View will be shown if - * this is true - */ - private boolean isDotViewEnabled; + private boolean gestureDrawableEnabled; + + private @DrawableRes int gestureDrawableResId; + + private @AnimatorRes int gestureAnimatorResId; + + private Animator gestureAnimator; /** * Info Dialog Icon */ private ImageView imageViewIcon; + private int iconDrawableResId; + /** * Image View will be shown if * this is true @@ -261,10 +255,13 @@ private void init(Context context) { dismissOnTouch = false; isLayoutCompleted = false; isInfoEnabled = false; - isDotViewEnabled = false; isPerformClick = false; isImageViewEnabled = true; isIdempotent = false; + gestureDrawableEnabled = true; + gestureDrawableResId = Constants.DEFAULT_GESTURE_DRAWABLE; + gestureAnimatorResId = Constants.DEFAULT_GESTURE_ANIMATOR; + iconDrawableResId = Constants.DEFAULT_ICON_DRAWABLE; /** * initialize objects @@ -273,20 +270,16 @@ private void init(Context context) { preferencesManager = new PreferencesManager(context); - eraser = new Paint(); - eraser.setColor(0xFFFFFFFF); - eraser.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); - eraser.setFlags(Paint.ANTI_ALIAS_FLAG); - View layoutInfo = LayoutInflater.from(getContext()).inflate(R.layout.material_intro_card, null); infoView = layoutInfo.findViewById(R.id.info_layout); textViewInfo = (TextView) layoutInfo.findViewById(R.id.textview_info); textViewInfo.setTextColor(colorTextViewInfo); imageViewIcon = (ImageView) layoutInfo.findViewById(R.id.imageview_icon); + imageViewIcon.setImageResource(iconDrawableResId); - dotView = LayoutInflater.from(getContext()).inflate(R.layout.dotview, null); - dotView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); + gestureView = (ImageView) LayoutInflater.from(getContext()).inflate(R.layout.gestureview, null); + gestureView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override @@ -295,8 +288,8 @@ public void onGlobalLayout() { if (targetShape != null && targetShape.getPoint().y != 0 && !isLayoutCompleted) { if (isInfoEnabled) setInfoLayout(); - if(isDotViewEnabled) - setDotViewLayout(); + if(gestureDrawableEnabled && gestureDrawableResId != 0) + setGestureViewLayout(); removeOnGlobalLayoutListener(MaterialIntroView.this, this); } } @@ -327,25 +320,18 @@ protected void onDraw(Canvas canvas) { if (!isReady) return; - if (bitmap == null || canvas == null) { - if (bitmap != null) bitmap.recycle(); + canvas.save(); - bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - this.canvas = new Canvas(bitmap); + if (targetShapePath == null) { + targetShapePath = new Path(); + targetShapePath.addPath(targetShape.getPath(this.padding)); + targetShapePath.setFillType(Path.FillType.INVERSE_EVEN_ODD); } + // TODO add antialiasing somehow, clipPath() does not support it + canvas.clipPath(targetShapePath); + canvas.drawColor(maskColor); - /** - * Draw mask - */ - this.canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); - this.canvas.drawColor(maskColor); - - /** - * Clear focus area - */ - targetShape.draw(this.canvas, eraser, padding); - - canvas.drawBitmap(bitmap, 0, 0, null); + canvas.restore(); } /** @@ -361,32 +347,45 @@ public boolean onTouchEvent(MotionEvent event) { float yT = event.getY(); boolean isTouchOnFocus = targetShape.isTouchOnFocus(xT, yT); + // TODO extract the flag into MaterialIntroView method ? + boolean passEventsThroughFocusShape = gestureDrawableResId != Constants.DEFAULT_GESTURE_DRAWABLE; - switch (event.getAction()) { - case MotionEvent.ACTION_DOWN: - - if (isTouchOnFocus && isPerformClick) { - targetView.getView().setPressed(true); - targetView.getView().invalidate(); - } - + if (passEventsThroughFocusShape) { + if (!isTouchOnFocus) return true; - case MotionEvent.ACTION_UP: - if (isTouchOnFocus || dismissOnTouch) - dismiss(); - - if (isTouchOnFocus && isPerformClick) { - targetView.getView().performClick(); - targetView.getView().setPressed(true); - targetView.getView().invalidate(); - targetView.getView().setPressed(false); - targetView.getView().invalidate(); - } - - return true; - default: - break; + } else { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + + if (isTouchOnFocus && isPerformClick) { + targetView.getView().setPressed(true); + targetView.getView().invalidate(); + + return true; + } + break; + + case MotionEvent.ACTION_UP: + + if (isTouchOnFocus || dismissOnTouch) + dismiss(); + + if (isTouchOnFocus && isPerformClick) { + targetView.getView().performClick(); + targetView.getView().setPressed(true); + targetView.getView().invalidate(); + targetView.getView().setPressed(false); + targetView.getView().invalidate(); + } + + if (isTouchOnFocus) { + return true; + } + break; + default: + break; + } } return super.onTouchEvent(event); @@ -472,15 +471,24 @@ public void run() { ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.FILL_PARENT); + RelativeLayout infoRelativeLayout = ((RelativeLayout) infoView); + if (targetShape.getPoint().x < width / 3) { + infoRelativeLayout.setHorizontalGravity(Gravity.START); + } else if (targetShape.getPoint().x > (2 * width) / 3) { + infoRelativeLayout.setHorizontalGravity(Gravity.END); + } else { + infoRelativeLayout.setHorizontalGravity(Gravity.CENTER_HORIZONTAL); + } + if (targetShape.getPoint().y < height / 2) { - ((RelativeLayout) infoView).setGravity(Gravity.TOP); + ((RelativeLayout) infoView).setVerticalGravity(Gravity.TOP); infoDialogParams.setMargins( 0, targetShape.getPoint().y + targetShape.getHeight() / 2, 0, 0); } else { - ((RelativeLayout) infoView).setGravity(Gravity.BOTTOM); + ((RelativeLayout) infoView).setVerticalGravity(Gravity.BOTTOM); infoDialogParams.setMargins( 0, 0, @@ -493,38 +501,54 @@ public void run() { addView(infoView); - if (!isImageViewEnabled){ - imageViewIcon.setVisibility(GONE); + imageViewIcon.setVisibility(isImageViewEnabled ? VISIBLE : GONE); + imageViewIcon.setImageResource(iconDrawableResId); + if (iconDrawableResId != Constants.DEFAULT_ICON_DRAWABLE) { + LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) imageViewIcon.getLayoutParams(); + lp.width = ViewGroup.LayoutParams.WRAP_CONTENT; + lp.height = ViewGroup.LayoutParams.WRAP_CONTENT; + imageViewIcon.setLayoutParams(lp); } + infoView.setVisibility(VISIBLE); } }); } - private void setDotViewLayout() { + private void setGestureViewLayout() { handler.post(new Runnable() { @Override public void run() { - if (dotView.getParent() != null) - ((ViewGroup) dotView.getParent()).removeView(dotView); + if (gestureView.getParent() != null) + ((ViewGroup) gestureView.getParent()).removeView(gestureView); - RelativeLayout.LayoutParams dotViewLayoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); - dotViewLayoutParams.height = Utils.dpToPx(Constants.DEFAULT_DOT_SIZE); - dotViewLayoutParams.width = Utils.dpToPx(Constants.DEFAULT_DOT_SIZE); - dotViewLayoutParams.setMargins( - targetShape.getPoint().x - (dotViewLayoutParams.width / 2), - targetShape.getPoint().y - (dotViewLayoutParams.height / 2), + RelativeLayout.LayoutParams gestureViewLayoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + gestureViewLayoutParams.height = Utils.dpToPx(Constants.DEFAULT_DOT_SIZE); + gestureViewLayoutParams.width = Utils.dpToPx(Constants.DEFAULT_DOT_SIZE); + gestureViewLayoutParams.setMargins( + targetShape.getPoint().x - (gestureViewLayoutParams.width / 2), + targetShape.getPoint().y - (gestureViewLayoutParams.height / 2), 0, 0); - dotView.setLayoutParams(dotViewLayoutParams); - dotView.postInvalidate(); - addView(dotView); + gestureView.setLayoutParams(gestureViewLayoutParams); + gestureView.postInvalidate(); + addView(gestureView); + gestureView.setVisibility(VISIBLE); - dotView.setVisibility(VISIBLE); - AnimationFactory.performAnimation(dotView); + if (gestureDrawableResId != 0) { + gestureView.setImageResource(gestureDrawableResId); + } + + if (gestureAnimatorResId != 0) { + if (gestureAnimator == null) { + gestureAnimator = AnimatorInflater.loadAnimator(getContext(), gestureAnimatorResId); + } + gestureAnimator.setTarget(gestureView); + gestureAnimator.start(); + } } }); } @@ -545,6 +569,10 @@ private void enableFadeAnimation(boolean isFadeAnimationEnabled) { this.isFadeAnimationEnabled = isFadeAnimationEnabled; } + private void setFadeAnimationDuration(long fadeAnimationDuration) { + this.fadeAnimationDuration = fadeAnimationDuration; + } + private void setShapeType(ShapeType shape) { this.shapeType = shape; } @@ -567,6 +595,7 @@ private void setShape(Shape shape) { private void setPadding(int padding) { this.padding = padding; + this.targetShapePath = null; } private void setDismissOnTouch(boolean dismissOnTouch) { @@ -598,12 +627,40 @@ private void enableImageViewIcon(boolean isImageViewEnabled){ this.isImageViewEnabled = isImageViewEnabled; } + public int getIconDrawableResId() { + return iconDrawableResId; + } + + public void setIconDrawableResId(int iconDrawableResId) { + this.iconDrawableResId = iconDrawableResId; + } + private void setIdempotent(boolean idempotent){ this.isIdempotent = idempotent; } - private void enableDotView(boolean isDotViewEnabled){ - this.isDotViewEnabled = isDotViewEnabled; + public boolean isGestureDrawableEnabled() { + return gestureDrawableEnabled; + } + + public void enableGestureDrawable(boolean enabled) { + this.gestureDrawableEnabled = enabled; + } + + public int getGestureDrawableResId() { + return gestureDrawableResId; + } + + public void setGestureDrawableResId(int gestureDrawableResId) { + this.gestureDrawableResId = gestureDrawableResId; + } + + public int getGestureAnimatorResId() { + return gestureAnimatorResId; + } + + public void setGestureAnimatorResId(int gestureAnimatorResId) { + this.gestureAnimatorResId = gestureAnimatorResId; } public void setConfiguration(MaterialIntroConfiguration configuration) { @@ -613,11 +670,14 @@ public void setConfiguration(MaterialIntroConfiguration configuration) { this.delayMillis = configuration.getDelayMillis(); this.isFadeAnimationEnabled = configuration.isFadeAnimationEnabled(); this.colorTextViewInfo = configuration.getColorTextViewInfo(); - this.isDotViewEnabled = configuration.isDotViewEnabled(); + this.gestureDrawableResId = configuration.getGestureDrawableResId(); + this.gestureAnimatorResId = configuration.getGestureAnimatorResId(); this.dismissOnTouch = configuration.isDismissOnTouch(); this.colorTextViewInfo = configuration.getColorTextViewInfo(); this.focusType = configuration.getFocusType(); this.focusGravity = configuration.getFocusGravity(); + this.isImageViewEnabled = configuration.isImageViewEnabled(); + this.iconDrawableResId = configuration.getIconDrawableResId(); } } @@ -664,6 +724,11 @@ public Builder enableFadeAnimation(boolean isFadeAnimationEnabled) { return this; } + public Builder setFadeAnimationDuration(long fadeAnimationDuration) { + materialIntroView.setFadeAnimationDuration(fadeAnimationDuration); + return this; + } + public Builder setShape(ShapeType shape) { materialIntroView.setShapeType(shape); return this; @@ -715,8 +780,18 @@ public Builder setUsageId(String materialIntroViewId) { return this; } - public Builder enableDotAnimation(boolean isDotAnimationEnabled) { - materialIntroView.enableDotView(isDotAnimationEnabled); + public Builder enableGestureDrawable(boolean enabled) { + materialIntroView.enableGestureDrawable(enabled); + return this; + } + + public Builder setGestureDrawableResId(@DrawableRes int gestureDrawableResId) { + materialIntroView.setGestureDrawableResId(gestureDrawableResId); + return this; + } + + public Builder setGestureAnimatorResId(@AnimatorRes int gestureAnimatorResId) { + materialIntroView.setGestureAnimatorResId(gestureAnimatorResId); return this; } @@ -725,6 +800,11 @@ public Builder enableIcon(boolean isImageViewIconEnabled) { return this; } + public Builder setIconDrawableResId(@DrawableRes int drawableResId) { + materialIntroView.setIconDrawableResId(drawableResId); + return this; + } + public Builder setIdempotent(boolean idempotent) { materialIntroView.setIdempotent(idempotent); return this; diff --git a/materialintro/src/main/res/animator/pulsate.xml b/materialintro/src/main/res/animator/pulsate.xml new file mode 100644 index 0000000..5c18cd6 --- /dev/null +++ b/materialintro/src/main/res/animator/pulsate.xml @@ -0,0 +1,20 @@ + + + + + + + \ No newline at end of file diff --git a/materialintro/src/main/res/drawable-hdpi/ic_dot.png b/materialintro/src/main/res/drawable-hdpi/ic_dot.png new file mode 100644 index 0000000..a573179 Binary files /dev/null and b/materialintro/src/main/res/drawable-hdpi/ic_dot.png differ diff --git a/materialintro/src/main/res/drawable-mdpi/ic_dot.png b/materialintro/src/main/res/drawable-mdpi/ic_dot.png new file mode 100644 index 0000000..748026b Binary files /dev/null and b/materialintro/src/main/res/drawable-mdpi/ic_dot.png differ diff --git a/materialintro/src/main/res/drawable-xhdpi/ic_dot.png b/materialintro/src/main/res/drawable-xhdpi/ic_dot.png new file mode 100644 index 0000000..18929ff Binary files /dev/null and b/materialintro/src/main/res/drawable-xhdpi/ic_dot.png differ diff --git a/materialintro/src/main/res/drawable-xxhdpi/ic_dot.png b/materialintro/src/main/res/drawable-xxhdpi/ic_dot.png new file mode 100644 index 0000000..b955566 Binary files /dev/null and b/materialintro/src/main/res/drawable-xxhdpi/ic_dot.png differ diff --git a/materialintro/src/main/res/drawable-xxxhdpi/ic_dot.png b/materialintro/src/main/res/drawable-xxxhdpi/ic_dot.png new file mode 100644 index 0000000..0d5f9a5 Binary files /dev/null and b/materialintro/src/main/res/drawable-xxxhdpi/ic_dot.png differ diff --git a/materialintro/src/main/res/drawable/icon_dotview.png b/materialintro/src/main/res/drawable/icon_dotview.png deleted file mode 100644 index 7e7b16e..0000000 Binary files a/materialintro/src/main/res/drawable/icon_dotview.png and /dev/null differ diff --git a/materialintro/src/main/res/layout/dotview.xml b/materialintro/src/main/res/layout/gestureview.xml similarity index 86% rename from materialintro/src/main/res/layout/dotview.xml rename to materialintro/src/main/res/layout/gestureview.xml index afbdadf..7c7b93b 100644 --- a/materialintro/src/main/res/layout/dotview.xml +++ b/materialintro/src/main/res/layout/gestureview.xml @@ -6,7 +6,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:src="@drawable/icon_dotview" + android:src="@drawable/ic_dot" android:visibility="invisible"/> diff --git a/materialintro/src/main/res/layout/material_intro_card.xml b/materialintro/src/main/res/layout/material_intro_card.xml index fa5fce9..2d6d849 100644 --- a/materialintro/src/main/res/layout/material_intro_card.xml +++ b/materialintro/src/main/res/layout/material_intro_card.xml @@ -1,52 +1,52 @@ + app:contentPadding="16dp"> - + android:layout_height="wrap_content"> + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_gravity="top" + android:layout_marginRight="16dp" + android:layout_marginEnd="16dp" + tools:src="@drawable/icon_question" + /> + android:textColor="@android:color/black" + android:textSize="@dimen/info_text_size" + tools:text="Long long long long long long long long long long long long long long long hint text" /> - + diff --git a/materialintro/src/main/res/values/dimens.xml b/materialintro/src/main/res/values/dimens.xml index a8b38b0..112ffa4 100644 --- a/materialintro/src/main/res/values/dimens.xml +++ b/materialintro/src/main/res/values/dimens.xml @@ -2,4 +2,5 @@ 24dp 16sp + 96dp \ No newline at end of file diff --git a/sample/build.gradle b/sample/build.gradle index ea5e823..7e50f46 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -1,13 +1,13 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 23 - buildToolsVersion "23.0.2" + compileSdkVersion 26 + buildToolsVersion "26.0.1" defaultConfig { applicationId "co.mobiwise.sample" minSdkVersion 14 - targetSdkVersion 23 + targetSdkVersion 26 versionCode 1 versionName "1.0" } @@ -22,8 +22,8 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' - compile 'com.android.support:appcompat-v7:23.1.1' - compile 'com.android.support:design:23.1.1' + compile 'com.android.support:appcompat-v7:26.0.1' + compile 'com.android.support:design:26.0.1' //compile 'com.github.iammert:MaterialIntroView:1.5.1' compile project(':materialintro') compile 'com.squareup.picasso:picasso:2.5.2' diff --git a/sample/src/main/java/co/mobiwise/sample/MainActivity.java b/sample/src/main/java/co/mobiwise/sample/MainActivity.java index 2a305ff..370f6b0 100644 --- a/sample/src/main/java/co/mobiwise/sample/MainActivity.java +++ b/sample/src/main/java/co/mobiwise/sample/MainActivity.java @@ -12,6 +12,7 @@ import android.view.MenuItem; import co.mobiwise.sample.fragment.FocusFragment; +import co.mobiwise.sample.fragment.GestureFragment; import co.mobiwise.sample.fragment.GravityFragment; import co.mobiwise.sample.fragment.MainFragment; import co.mobiwise.sample.fragment.RecyclerviewFragment; @@ -97,6 +98,9 @@ public boolean onNavigationItemSelected(MenuItem item) { break; case R.id.nav_tab: break; + case R.id.nav_gesture: + getSupportFragmentManager().beginTransaction().replace(R.id.container, new GestureFragment()).commit(); + break; default: break; } diff --git a/sample/src/main/java/co/mobiwise/sample/ToolbarMenuItemActivity.java b/sample/src/main/java/co/mobiwise/sample/ToolbarMenuItemActivity.java index 7835f6f..e6787ec 100644 --- a/sample/src/main/java/co/mobiwise/sample/ToolbarMenuItemActivity.java +++ b/sample/src/main/java/co/mobiwise/sample/ToolbarMenuItemActivity.java @@ -134,7 +134,6 @@ public boolean onNavigationItemSelected(MenuItem item) { */ public void showIntro(View view, String id, String text, FocusGravity focusGravity) { new MaterialIntroView.Builder(ToolbarMenuItemActivity.this) - .enableDotAnimation(true) .setFocusGravity(focusGravity) .setFocusType(Focus.MINIMUM) .setDelayMillis(100) diff --git a/sample/src/main/java/co/mobiwise/sample/fragment/FocusFragment.java b/sample/src/main/java/co/mobiwise/sample/fragment/FocusFragment.java index 82e9e86..9ee3eea 100644 --- a/sample/src/main/java/co/mobiwise/sample/fragment/FocusFragment.java +++ b/sample/src/main/java/co/mobiwise/sample/fragment/FocusFragment.java @@ -43,7 +43,6 @@ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, public void showIntro(View view, String id, String text, Focus focusType){ new MaterialIntroView.Builder(getActivity()) - .enableDotAnimation(false) .setFocusGravity(FocusGravity.CENTER) .setFocusType(focusType) .setDelayMillis(200) diff --git a/sample/src/main/java/co/mobiwise/sample/fragment/GestureFragment.java b/sample/src/main/java/co/mobiwise/sample/fragment/GestureFragment.java new file mode 100644 index 0000000..643fd94 --- /dev/null +++ b/sample/src/main/java/co/mobiwise/sample/fragment/GestureFragment.java @@ -0,0 +1,109 @@ +package co.mobiwise.sample.fragment; + +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.support.v4.view.PagerAdapter; +import android.support.v4.view.ViewPager; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import co.mobiwise.materialintro.animation.MaterialIntroListener; +import co.mobiwise.materialintro.shape.CircleOnEdge; +import co.mobiwise.materialintro.shape.FocusGravity; +import co.mobiwise.materialintro.target.ViewTarget; +import co.mobiwise.materialintro.view.MaterialIntroView; +import co.mobiwise.sample.R; + +/** + * Created by mertsimsek on 31/01/16. + */ +public class GestureFragment extends Fragment implements MaterialIntroListener{ + + private static final String INTRO_CARD_LEFT = "intro_gesture_left"; + private static final String INTRO_CARD_RIGHT = "intro_gesture_right"; + private static final String INTRO_CARD_TOP = "intro_gesture_top"; + private static final String INTRO_CARD_BOTTOM = "intro_gesture_bottom"; + + private ViewPager pager; + private PagerAdapter pagerAdapter; + + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_gesture, container, false); + + this.pager = view.findViewById(R.id.pager); + this.pagerAdapter = new ScreenSlidePagerAdapter(); + this.pager.setAdapter(this.pagerAdapter); + + showIntro(view, INTRO_CARD_RIGHT, "Hint that the view can be dragged from the RIGHT edge", + FocusGravity.RIGHT, R.animator.drag_from_right); + + return view; + } + + @Override + public void onUserClicked(String materialIntroViewId) { + if(materialIntroViewId == INTRO_CARD_RIGHT) { + pager.setCurrentItem(1, true); + showIntro(getView(), INTRO_CARD_LEFT, "Hint that the view can be dragged from the LEFT edge", + FocusGravity.LEFT, R.animator.drag_from_left); + } else if (materialIntroViewId == INTRO_CARD_LEFT) { + pager.setCurrentItem(0, true); + showIntro(getView(), INTRO_CARD_TOP, "Hint that the view can be dragged from the TOP edge", + FocusGravity.TOP, R.animator.drag_from_top); + } else if (materialIntroViewId == INTRO_CARD_TOP) { + showIntro(getView(), INTRO_CARD_BOTTOM, "Hint that the view can be dragged from the BOTTOM edge", + FocusGravity.BOTTOM, R.animator.drag_from_bottom); + } + } + + public void showIntro(View view, String id, String text, FocusGravity focusGravity, int animatorResId){ + new MaterialIntroView.Builder(getActivity()) + .setGestureDrawableResId(R.drawable.ic_cursor_pointer) + .setGestureAnimatorResId(animatorResId) + .setCustomShape(new CircleOnEdge(new ViewTarget(view), focusGravity, 0)) + .setDelayMillis(200) + .setIconDrawableResId(android.R.drawable.ic_dialog_info) + .enableFadeAnimation(true) + .setFadeAnimationDuration(200) + .performClick(true) + .setInfoText(text) + .setTarget(view) + .setListener(this) + .setUsageId(id) //THIS SHOULD BE UNIQUE ID + .show(); + } + + private class ScreenSlidePagerAdapter extends PagerAdapter { + + @Override + public Object instantiateItem(ViewGroup collection, int position) { + LayoutInflater inflater = LayoutInflater.from(getContext()); + TextView pageNumber = (TextView) inflater.inflate(R.layout.page, collection, false); + pageNumber.setBackgroundColor(position == 0 ? 0xFF00897B : 0xFFFF9800); + pageNumber.setText(String.valueOf(position)); + collection.addView(pageNumber); + return pageNumber; + } + + @Override + public void destroyItem(ViewGroup collection, int position, Object view) { + collection.removeView((View) view); + } + + @Override + public int getCount() { + return 2; + } + + @Override + public boolean isViewFromObject(View view, Object object) { + return view == object; + } + } +} diff --git a/sample/src/main/java/co/mobiwise/sample/fragment/GravityFragment.java b/sample/src/main/java/co/mobiwise/sample/fragment/GravityFragment.java index dbe4be2..e9fc0b7 100644 --- a/sample/src/main/java/co/mobiwise/sample/fragment/GravityFragment.java +++ b/sample/src/main/java/co/mobiwise/sample/fragment/GravityFragment.java @@ -51,7 +51,6 @@ public void onUserClicked(String materialIntroViewId) { public void showIntro(View view, String id, String text, FocusGravity focusGravity){ new MaterialIntroView.Builder(getActivity()) - .enableDotAnimation(true) .setFocusGravity(focusGravity) .setFocusType(Focus.MINIMUM) .setDelayMillis(200) diff --git a/sample/src/main/java/co/mobiwise/sample/fragment/MainFragment.java b/sample/src/main/java/co/mobiwise/sample/fragment/MainFragment.java index 3d829cd..e67308b 100644 --- a/sample/src/main/java/co/mobiwise/sample/fragment/MainFragment.java +++ b/sample/src/main/java/co/mobiwise/sample/fragment/MainFragment.java @@ -50,7 +50,6 @@ public void onClick(View v) { private void showIntro(View view, String usageId, String text){ new MaterialIntroView.Builder(getActivity()) - .enableDotAnimation(true) //.enableIcon(false) .setFocusGravity(FocusGravity.CENTER) .setFocusType(Focus.MINIMUM) diff --git a/sample/src/main/java/co/mobiwise/sample/fragment/RecyclerviewFragment.java b/sample/src/main/java/co/mobiwise/sample/fragment/RecyclerviewFragment.java index 4e7aed0..ab8a144 100644 --- a/sample/src/main/java/co/mobiwise/sample/fragment/RecyclerviewFragment.java +++ b/sample/src/main/java/co/mobiwise/sample/fragment/RecyclerviewFragment.java @@ -49,7 +49,6 @@ public void run() { private void showMaterialIntro() { new MaterialIntroView.Builder(getActivity()) - .enableDotAnimation(true) .setFocusGravity(FocusGravity.CENTER) .setFocusType(Focus.MINIMUM) .setDelayMillis(200) diff --git a/sample/src/main/res/animator/drag_from_bottom.xml b/sample/src/main/res/animator/drag_from_bottom.xml new file mode 100644 index 0000000..0665c61 --- /dev/null +++ b/sample/src/main/res/animator/drag_from_bottom.xml @@ -0,0 +1,10 @@ + + \ No newline at end of file diff --git a/sample/src/main/res/animator/drag_from_left.xml b/sample/src/main/res/animator/drag_from_left.xml new file mode 100644 index 0000000..97bd54f --- /dev/null +++ b/sample/src/main/res/animator/drag_from_left.xml @@ -0,0 +1,10 @@ + + \ No newline at end of file diff --git a/sample/src/main/res/animator/drag_from_right.xml b/sample/src/main/res/animator/drag_from_right.xml new file mode 100644 index 0000000..e98be1d --- /dev/null +++ b/sample/src/main/res/animator/drag_from_right.xml @@ -0,0 +1,10 @@ + + \ No newline at end of file diff --git a/sample/src/main/res/animator/drag_from_top.xml b/sample/src/main/res/animator/drag_from_top.xml new file mode 100644 index 0000000..7ae9cbc --- /dev/null +++ b/sample/src/main/res/animator/drag_from_top.xml @@ -0,0 +1,10 @@ + + \ No newline at end of file diff --git a/sample/src/main/res/drawable/ic_cursor_pointer.xml b/sample/src/main/res/drawable/ic_cursor_pointer.xml new file mode 100644 index 0000000..88bc60c --- /dev/null +++ b/sample/src/main/res/drawable/ic_cursor_pointer.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/sample/src/main/res/layout/fragment_gesture.xml b/sample/src/main/res/layout/fragment_gesture.xml new file mode 100644 index 0000000..80a99db --- /dev/null +++ b/sample/src/main/res/layout/fragment_gesture.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/sample/src/main/res/layout/page.xml b/sample/src/main/res/layout/page.xml new file mode 100644 index 0000000..1a1df4b --- /dev/null +++ b/sample/src/main/res/layout/page.xml @@ -0,0 +1,9 @@ + + \ No newline at end of file diff --git a/sample/src/main/res/menu/activity_main_drawer.xml b/sample/src/main/res/menu/activity_main_drawer.xml index 1b8e46b..61d9b52 100644 --- a/sample/src/main/res/menu/activity_main_drawer.xml +++ b/sample/src/main/res/menu/activity_main_drawer.xml @@ -20,6 +20,9 @@ +