From 41e4f425f73eecdcbbbc8a571c7927532c47a772 Mon Sep 17 00:00:00 2001 From: Md Hanif Date: Mon, 7 Jun 2021 21:03:56 +0530 Subject: [PATCH] Migrate to AndroidX --- .idea/compiler.xml | 6 + .idea/gradle.xml | 2 + .idea/jarRepositories.xml | 30 ++ .idea/misc.xml | 2 +- .idea/modules.xml | 8 +- .idea/runConfigurations.xml | 12 - app/build.gradle | 25 +- .../bubbleshowcase/ExampleInstrumentedTest.kt | 8 +- .../bubbleshowcase/MainActivity.kt | 6 +- app/src/main/res/layout/activity_main.xml | 4 +- bubbleshowcase/build.gradle | 38 +- bubbleshowcase/deploy.gradle | 2 +- .../ExampleInstrumentedTest.java | 7 +- bubbleshowcase/src/main/AndroidManifest.xml | 3 +- .../bubbleshowcase/AnimationUtils.kt | 18 +- .../bubbleshowcase/BubbleMessageView.kt | 135 ++++--- .../bubbleshowcase/BubbleShowCase.kt | 378 +++++++++++------- .../bubbleshowcase/BubbleShowCaseBuilder.kt | 38 +- .../bubbleshowcase/BubbleShowCaseSequence.kt | 14 +- .../OnBubbleMessageViewListener.kt | 1 - .../bubbleshowcase/ScreenUtils.kt | 9 +- .../main/res/drawable/rounded_rectangle.xml | 10 +- .../main/res/layout/view_bubble_message.xml | 27 +- build.gradle | 8 +- gradle.properties | 2 + gradle/wrapper/gradle-wrapper.properties | 2 +- 26 files changed, 468 insertions(+), 327 deletions(-) create mode 100644 .idea/compiler.xml create mode 100644 .idea/jarRepositories.xml delete mode 100644 .idea/runConfigurations.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..61a9130 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index fc9576b..7b24c94 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -1,8 +1,10 @@ + - + diff --git a/.idea/modules.xml b/.idea/modules.xml index 593b872..440ef89 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,11 +2,9 @@ - - - - - + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml deleted file mode 100644 index 7f68460..0000000 --- a/.idea/runConfigurations.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 70a71c1..9feac4f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,18 +1,16 @@ apply plugin: 'com.android.application' - apply plugin: 'kotlin-android' - apply plugin: 'kotlin-android-extensions' android { - compileSdkVersion 27 + compileSdkVersion 30 defaultConfig { applicationId "com.elconfidencial.bubbleshowcase" - minSdkVersion 16 - targetSdkVersion 27 + minSdkVersion 21 + targetSdkVersion 30 versionCode 1 versionName "1.0" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' } buildTypes { release { @@ -25,11 +23,12 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation project(':bubbleshowcase') - implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'com.android.support:design:27.1.1' - implementation 'com.android.support:appcompat-v7:27.1.1' - implementation 'com.android.support.constraint:constraint-layout:1.1.3' - testImplementation 'junit:junit:4.12' - androidTestImplementation 'com.android.support.test:runner:1.0.2' - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation 'androidx.core:core-ktx:1.3.2' + implementation 'com.google.android.material:material:1.2.1' + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'androidx.constraintlayout:constraintlayout:2.0.4' + testImplementation 'junit:junit:4.13' + androidTestImplementation 'androidx.test.ext:junit:1.1.2' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' } diff --git a/app/src/androidTest/java/com/elconfidencial/bubbleshowcase/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/elconfidencial/bubbleshowcase/ExampleInstrumentedTest.kt index a4e402d..2f8d62f 100644 --- a/app/src/androidTest/java/com/elconfidencial/bubbleshowcase/ExampleInstrumentedTest.kt +++ b/app/src/androidTest/java/com/elconfidencial/bubbleshowcase/ExampleInstrumentedTest.kt @@ -1,13 +1,11 @@ package com.elconfidencial.bubbleshowcase -import android.support.test.InstrumentationRegistry -import android.support.test.runner.AndroidJUnit4 - +import androidx.test.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith -import org.junit.Assert.* - /** * Instrumented test, which will execute on an Android device. * diff --git a/app/src/main/java/com/elconfidencial/bubbleshowcase/MainActivity.kt b/app/src/main/java/com/elconfidencial/bubbleshowcase/MainActivity.kt index a8d2e5e..fa0a817 100644 --- a/app/src/main/java/com/elconfidencial/bubbleshowcase/MainActivity.kt +++ b/app/src/main/java/com/elconfidencial/bubbleshowcase/MainActivity.kt @@ -1,9 +1,9 @@ package com.elconfidencial.bubbleshowcase -import android.support.v7.app.AppCompatActivity +import androidx.appcompat.app.AppCompatActivity import android.os.Bundle -import android.support.v4.content.ContextCompat -import android.support.v7.view.menu.ActionMenuItemView +import androidx.core.content.ContextCompat +import androidx.appcompat.view.menu.ActionMenuItemView import android.view.Menu import android.view.MenuItem import android.widget.Toast diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 7c170c9..9fc0480 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,5 +1,5 @@ - - + diff --git a/bubbleshowcase/build.gradle b/bubbleshowcase/build.gradle index e95061c..6099257 100644 --- a/bubbleshowcase/build.gradle +++ b/bubbleshowcase/build.gradle @@ -3,24 +3,16 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' apply plugin: 'com.github.dcendents.android-maven' apply plugin: 'com.jfrog.bintray' -apply plugin: 'org.jetbrains.dokka' apply plugin: 'maven-publish' - - android { - compileSdkVersion 26 - - - + compileSdkVersion 30 defaultConfig { - minSdkVersion 16 - targetSdkVersion 26 + minSdkVersion 21 + targetSdkVersion 30 versionCode 1 versionName "1.0" - - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" - + testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' } buildTypes { @@ -29,26 +21,20 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } - -} - -dokka { - outputFormat = 'html' - outputDirectory = "$buildDir/javadoc" } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'com.android.support.constraint:constraint-layout:1.0.2' - implementation 'com.android.support:appcompat-v7:26.1.0' - testImplementation 'junit:junit:4.12' - androidTestImplementation 'com.android.support.test:runner:1.0.2' - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation 'androidx.core:core-ktx:1.3.2' + implementation 'androidx.constraintlayout:constraintlayout:2.0.4' + implementation 'androidx.appcompat:appcompat:1.2.0' + testImplementation 'junit:junit:4.13' + androidTestImplementation 'androidx.test.ext:junit:1.1.2' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' } repositories { mavenCentral() } - -apply from: 'deploy.gradle' +//apply from: 'deploy.gradle' diff --git a/bubbleshowcase/deploy.gradle b/bubbleshowcase/deploy.gradle index cbf8cc6..306b392 100644 --- a/bubbleshowcase/deploy.gradle +++ b/bubbleshowcase/deploy.gradle @@ -46,7 +46,7 @@ if (project.hasProperty("kotlin")) { //Kotlin libraries from android.sourceSets.main.java.srcDirs } - task javadoc(type: Javadoc, dependsOn: dokka) { + task javadoc(type: Javadoc) { } } else if (project.hasProperty("android")) { diff --git a/bubbleshowcase/src/androidTest/java/com/elconfidencial/bubbleshowcase/ExampleInstrumentedTest.java b/bubbleshowcase/src/androidTest/java/com/elconfidencial/bubbleshowcase/ExampleInstrumentedTest.java index 979846c..c13dd53 100644 --- a/bubbleshowcase/src/androidTest/java/com/elconfidencial/bubbleshowcase/ExampleInstrumentedTest.java +++ b/bubbleshowcase/src/androidTest/java/com/elconfidencial/bubbleshowcase/ExampleInstrumentedTest.java @@ -1,13 +1,14 @@ package com.elconfidencial.bubbleshowcase; import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; /** * Instrumented test, which will execute on an Android device. diff --git a/bubbleshowcase/src/main/AndroidManifest.xml b/bubbleshowcase/src/main/AndroidManifest.xml index ba35b60..a822177 100644 --- a/bubbleshowcase/src/main/AndroidManifest.xml +++ b/bubbleshowcase/src/main/AndroidManifest.xml @@ -1,2 +1 @@ - + diff --git a/bubbleshowcase/src/main/java/com/elconfidencial/bubbleshowcase/AnimationUtils.kt b/bubbleshowcase/src/main/java/com/elconfidencial/bubbleshowcase/AnimationUtils.kt index b9224a8..c68029e 100644 --- a/bubbleshowcase/src/main/java/com/elconfidencial/bubbleshowcase/AnimationUtils.kt +++ b/bubbleshowcase/src/main/java/com/elconfidencial/bubbleshowcase/AnimationUtils.kt @@ -15,10 +15,11 @@ object AnimationUtils { fun getScaleAnimation(offset: Int, duration: Int): Animation { val anim = ScaleAnimation( - 0f, 1f, // Start and end values for the X axis scaling - 0f, 1f, // Start and end values for the Y axis scaling - Animation.RELATIVE_TO_SELF, 0.5f, // Pivot point of X scaling - Animation.RELATIVE_TO_SELF, 0.5f) // Pivot point of Y scaling + 0f, 1f, // Start and end values for the X axis scaling + 0f, 1f, // Start and end values for the Y axis scaling + Animation.RELATIVE_TO_SELF, 0.5f, // Pivot point of X scaling + Animation.RELATIVE_TO_SELF, 0.5f + ) // Pivot point of Y scaling anim.fillAfter = true anim.startOffset = offset.toLong() anim.duration = duration.toLong() @@ -34,10 +35,11 @@ object AnimationUtils { } fun setBouncingAnimation(view: View, offset: Int, duration: Int): View { - - val objAnim = ObjectAnimator.ofPropertyValuesHolder(view, - PropertyValuesHolder.ofFloat("scaleX", 1.05f), - PropertyValuesHolder.ofFloat("scaleY", 1.05f)) + val objAnim = ObjectAnimator.ofPropertyValuesHolder( + view, + PropertyValuesHolder.ofFloat("scaleX", 1.05f), + PropertyValuesHolder.ofFloat("scaleY", 1.05f) + ) objAnim.duration = duration.toLong() objAnim.startDelay = offset.toLong() objAnim.repeatCount = ObjectAnimator.INFINITE diff --git a/bubbleshowcase/src/main/java/com/elconfidencial/bubbleshowcase/BubbleMessageView.kt b/bubbleshowcase/src/main/java/com/elconfidencial/bubbleshowcase/BubbleMessageView.kt index 43bf422..a1b640b 100644 --- a/bubbleshowcase/src/main/java/com/elconfidencial/bubbleshowcase/BubbleMessageView.kt +++ b/bubbleshowcase/src/main/java/com/elconfidencial/bubbleshowcase/BubbleMessageView.kt @@ -6,25 +6,22 @@ import android.graphics.Paint import android.graphics.Path import android.graphics.RectF import android.graphics.drawable.Drawable -import android.support.constraint.ConstraintLayout -import android.support.v4.content.ContextCompat import android.util.AttributeSet import android.util.TypedValue import android.view.View import android.widget.ImageView import android.widget.TextView +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.content.ContextCompat import java.lang.ref.WeakReference +import java.util.* -import java.util.ArrayList +private const val WIDTH_ARROW = 20 /** * Created by jcampos on 05/09/2018. */ - class BubbleMessageView : ConstraintLayout { - - private val WIDTH_ARROW = 20 - private var itemView: View? = null private var imageViewIcon: ImageView? = null @@ -72,17 +69,17 @@ class BubbleMessageView : ConstraintLayout { showCaseMessageViewLayout = findViewById(R.id.showCaseMessageViewLayout) } - private fun setAttributes(builder: Builder){ - if(builder.mImage!=null){ + private fun setAttributes(builder: Builder) { + if (builder.mImage != null) { imageViewIcon?.visibility = View.VISIBLE imageViewIcon?.setImageDrawable(builder.mImage!!) } - if(builder.mCloseAction!=null){ + if (builder.mCloseAction != null) { imageViewClose?.visibility = View.VISIBLE imageViewClose?.setImageDrawable(builder.mCloseAction!!) } - if(builder.mDisableCloseAction!=null && builder.mDisableCloseAction!!){ + if (builder.mDisableCloseAction != null && builder.mDisableCloseAction!!) { imageViewClose?.visibility = View.INVISIBLE } @@ -99,35 +96,34 @@ class BubbleMessageView : ConstraintLayout { textViewSubtitle?.setTextColor(builder.mTextColor!!) } builder.mTitleTextSize?.let { - textViewTitle?.setTextSize(TypedValue.COMPLEX_UNIT_SP, builder.mTitleTextSize!!.toFloat()) + textViewTitle?.setTextSize( + TypedValue.COMPLEX_UNIT_SP, + builder.mTitleTextSize!!.toFloat() + ) } builder.mSubtitleTextSize?.let { - textViewSubtitle?.setTextSize(TypedValue.COMPLEX_UNIT_SP, builder.mSubtitleTextSize!!.toFloat()) + textViewSubtitle?.setTextSize( + TypedValue.COMPLEX_UNIT_SP, + builder.mSubtitleTextSize!!.toFloat() + ) } builder.mBackgroundColor?.let { mBackgroundColor = builder.mBackgroundColor!! } arrowPositionList = builder.mArrowPosition targetViewScreenLocation = builder.mTargetViewScreenLocation } - private fun setBubbleListener(builder: Builder){ - imageViewClose?.setOnClickListener {builder.mListener?.onCloseActionImageClick()} - itemView?.setOnClickListener {builder.mListener?.onBubbleClick()} + private fun setBubbleListener(builder: Builder) { + imageViewClose?.setOnClickListener { builder.mListener?.onCloseActionImageClick() } + itemView?.setOnClickListener { builder.mListener?.onBubbleClick() } } - //END REGION - - //REGION AUX FUNCTIONS - private fun getViewWidth(): Int = width private fun getMargin(): Int = ScreenUtils.dpToPx(20) - private fun getSecurityArrowMargin(): Int = getMargin() + ScreenUtils.dpToPx(2 * WIDTH_ARROW / 3) - - //END REGION - - //REGION SHOW ITEM + private fun getSecurityArrowMargin(): Int = + getMargin() + ScreenUtils.dpToPx(2 * WIDTH_ARROW / 3) override fun onDraw(canvas: Canvas) { super.onDraw(canvas) @@ -141,39 +137,58 @@ class BubbleMessageView : ConstraintLayout { } private fun prepareToDraw() { - paint = Paint(Paint.ANTI_ALIAS_FLAG) - paint!!.color = mBackgroundColor - paint!!.style = Paint.Style.FILL - paint!!.strokeWidth = 4.0f + paint = Paint(Paint.ANTI_ALIAS_FLAG).apply { + color = mBackgroundColor + style = Paint.Style.FILL + strokeWidth = 4.0f + } } private fun drawRectangle(canvas: Canvas) { - val rect = RectF(getMargin().toFloat(), - getMargin().toFloat(), - getViewWidth() - getMargin().toFloat(), - height - getMargin().toFloat()) + val rect = RectF( + getMargin().toFloat(), + getMargin().toFloat(), + getViewWidth() - getMargin().toFloat(), + height - getMargin().toFloat() + ) canvas.drawRoundRect(rect, 10f, 10f, paint!!) } - private fun drawArrow(canvas: Canvas, arrowPosition: BubbleShowCase.ArrowPosition, targetViewLocationOnScreen: RectF?) { + private fun drawArrow( + canvas: Canvas, + arrowPosition: BubbleShowCase.ArrowPosition, + targetViewLocationOnScreen: RectF? + ) { val xPosition: Int val yPosition: Int when (arrowPosition) { BubbleShowCase.ArrowPosition.LEFT -> { xPosition = getMargin() - yPosition = if(targetViewLocationOnScreen!=null) getArrowVerticalPositionDependingOnTarget(targetViewLocationOnScreen) else height / 2 + yPosition = + if (targetViewLocationOnScreen != null) getArrowVerticalPositionDependingOnTarget( + targetViewLocationOnScreen + ) else height / 2 } BubbleShowCase.ArrowPosition.RIGHT -> { xPosition = getViewWidth() - getMargin() - yPosition = if(targetViewLocationOnScreen!=null) getArrowVerticalPositionDependingOnTarget(targetViewLocationOnScreen) else height / 2 + yPosition = + if (targetViewLocationOnScreen != null) getArrowVerticalPositionDependingOnTarget( + targetViewLocationOnScreen + ) else height / 2 } BubbleShowCase.ArrowPosition.TOP -> { - xPosition = if(targetViewLocationOnScreen!=null) getArrowHorizontalPositionDependingOnTarget(targetViewLocationOnScreen) else width / 2 + xPosition = + if (targetViewLocationOnScreen != null) getArrowHorizontalPositionDependingOnTarget( + targetViewLocationOnScreen + ) else width / 2 yPosition = getMargin() } BubbleShowCase.ArrowPosition.BOTTOM -> { - xPosition = if(targetViewLocationOnScreen!=null) getArrowHorizontalPositionDependingOnTarget(targetViewLocationOnScreen) else width / 2 + xPosition = + if (targetViewLocationOnScreen != null) getArrowHorizontalPositionDependingOnTarget( + targetViewLocationOnScreen + ) else width / 2 yPosition = height - getMargin() } } @@ -184,9 +199,14 @@ class BubbleMessageView : ConstraintLayout { private fun getArrowHorizontalPositionDependingOnTarget(targetViewLocationOnScreen: RectF?): Int { val xPosition: Int when { - isOutOfRightBound(targetViewLocationOnScreen) -> xPosition = width - getSecurityArrowMargin() + isOutOfRightBound(targetViewLocationOnScreen) -> xPosition = + width - getSecurityArrowMargin() isOutOfLeftBound(targetViewLocationOnScreen) -> xPosition = getSecurityArrowMargin() - else -> xPosition = Math.round(targetViewLocationOnScreen!!.centerX() - ScreenUtils.getAxisXpositionOfViewOnScreen(this)) + else -> xPosition = Math.round( + targetViewLocationOnScreen!!.centerX() - ScreenUtils.getAxisXpositionOfViewOnScreen( + this + ) + ) } return xPosition } @@ -194,27 +214,40 @@ class BubbleMessageView : ConstraintLayout { private fun getArrowVerticalPositionDependingOnTarget(targetViewLocationOnScreen: RectF?): Int { val yPosition: Int when { - isOutOfBottomBound(targetViewLocationOnScreen) -> yPosition = height - getSecurityArrowMargin() + isOutOfBottomBound(targetViewLocationOnScreen) -> yPosition = + height - getSecurityArrowMargin() isOutOfTopBound(targetViewLocationOnScreen) -> yPosition = getSecurityArrowMargin() - else -> yPosition = Math.round(targetViewLocationOnScreen!!.centerY() + ScreenUtils.getStatusBarHeight(context) - ScreenUtils.getAxisYpositionOfViewOnScreen(this)) + else -> yPosition = Math.round( + targetViewLocationOnScreen!!.centerY() + ScreenUtils.getStatusBarHeight(context) - ScreenUtils.getAxisYpositionOfViewOnScreen( + this + ) + ) } return yPosition } private fun isOutOfRightBound(targetViewLocationOnScreen: RectF?): Boolean { - return targetViewLocationOnScreen!!.centerX() > ScreenUtils.getAxisXpositionOfViewOnScreen(this) + width - getSecurityArrowMargin() + return targetViewLocationOnScreen!!.centerX() > ScreenUtils.getAxisXpositionOfViewOnScreen( + this + ) + width - getSecurityArrowMargin() } private fun isOutOfLeftBound(targetViewLocationOnScreen: RectF?): Boolean { - return targetViewLocationOnScreen!!.centerX() < ScreenUtils.getAxisXpositionOfViewOnScreen(this) + getSecurityArrowMargin() + return targetViewLocationOnScreen!!.centerX() < ScreenUtils.getAxisXpositionOfViewOnScreen( + this + ) + getSecurityArrowMargin() } private fun isOutOfBottomBound(targetViewLocationOnScreen: RectF?): Boolean { - return targetViewLocationOnScreen!!.centerY() > ScreenUtils.getAxisYpositionOfViewOnScreen(this) + height - getSecurityArrowMargin() - ScreenUtils.getStatusBarHeight(context) + return targetViewLocationOnScreen!!.centerY() > ScreenUtils.getAxisYpositionOfViewOnScreen( + this + ) + height - getSecurityArrowMargin() - ScreenUtils.getStatusBarHeight(context) } private fun isOutOfTopBound(targetViewLocationOnScreen: RectF?): Boolean { - return targetViewLocationOnScreen!!.centerY() < ScreenUtils.getAxisYpositionOfViewOnScreen(this) + getSecurityArrowMargin() - ScreenUtils.getStatusBarHeight(context) + return targetViewLocationOnScreen!!.centerY() < ScreenUtils.getAxisYpositionOfViewOnScreen( + this + ) + getSecurityArrowMargin() - ScreenUtils.getStatusBarHeight(context) } @@ -238,7 +271,7 @@ class BubbleMessageView : ConstraintLayout { /** * Builder for BubbleMessageView class */ - class Builder{ + class Builder { lateinit var mContext: WeakReference var mTargetViewScreenLocation: RectF? = null var mImage: Drawable? = null @@ -250,10 +283,10 @@ class BubbleMessageView : ConstraintLayout { var mTextColor: Int? = null var mTitleTextSize: Int? = null var mSubtitleTextSize: Int? = null - var mArrowPosition = ArrayList() + var mArrowPosition = ArrayList() var mListener: OnBubbleMessageViewListener? = null - fun from(context: Context): Builder{ + fun from(context: Context): Builder { mContext = WeakReference(context) return this } @@ -283,7 +316,7 @@ class BubbleMessageView : ConstraintLayout { return this } - fun targetViewScreenLocation(targetViewLocationOnScreen: RectF): Builder{ + fun targetViewScreenLocation(targetViewLocationOnScreen: RectF): Builder { mTargetViewScreenLocation = targetViewLocationOnScreen return this } @@ -319,7 +352,7 @@ class BubbleMessageView : ConstraintLayout { return this } - fun build(): BubbleMessageView{ + fun build(): BubbleMessageView { return BubbleMessageView(mContext.get()!!, this) } } diff --git a/bubbleshowcase/src/main/java/com/elconfidencial/bubbleshowcase/BubbleShowCase.kt b/bubbleshowcase/src/main/java/com/elconfidencial/bubbleshowcase/BubbleShowCase.kt index f1460b5..c3c43a0 100644 --- a/bubbleshowcase/src/main/java/com/elconfidencial/bubbleshowcase/BubbleShowCase.kt +++ b/bubbleshowcase/src/main/java/com/elconfidencial/bubbleshowcase/BubbleShowCase.kt @@ -1,6 +1,5 @@ package com.elconfidencial.bubbleshowcase - import android.app.Activity import android.content.Context import android.content.Context.MODE_PRIVATE @@ -8,30 +7,29 @@ import android.content.SharedPreferences import android.graphics.Bitmap import android.graphics.RectF import android.graphics.drawable.Drawable -import android.os.Build import android.os.Handler -import android.support.v4.content.ContextCompat import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.RelativeLayout +import androidx.core.content.ContextCompat import java.lang.ref.WeakReference -/** - * Created by jcampos on 04/09/2018. - */ +private const val SHARED_PREFS_NAME = "BubbleShowCasePrefs" -class BubbleShowCase(builder: BubbleShowCaseBuilder){ - private val SHARED_PREFS_NAME = "BubbleShowCasePrefs" +private const val FOREGROUND_LAYOUT_ID = 731 - private val FOREGROUND_LAYOUT_ID = 731 +private const val DURATION_SHOW_CASE_ANIMATION = 200 //ms +private const val DURATION_BACKGROUND_ANIMATION = 700 //ms +private const val DURATION_BEATING_ANIMATION = 700 //ms - private val DURATION_SHOW_CASE_ANIMATION = 200 //ms - private val DURATION_BACKGROUND_ANIMATION = 700 //ms - private val DURATION_BEATING_ANIMATION = 700 //ms +private const val MAX_WIDTH_MESSAGE_VIEW_TABLET = 420 //dp - private val MAX_WIDTH_MESSAGE_VIEW_TABLET = 420 //dp +/** + * Created by jcampos on 04/09/2018. + */ +class BubbleShowCase(builder: BubbleShowCaseBuilder) { /** * Enum class which corresponds to each valid position for the BubbleMessageView arrow @@ -49,7 +47,6 @@ class BubbleShowCase(builder: BubbleShowCaseBuilder){ VIEW_LAYOUT, VIEW_SURFACE } - private val mActivity: WeakReference = builder.mActivity!! //BubbleMessageView params @@ -67,23 +64,23 @@ class BubbleShowCase(builder: BubbleShowCaseBuilder){ private val mHighlightMode: BubbleShowCase.HighlightMode? = builder.mHighlightMode private val mArrowPositionList: MutableList = builder.mArrowPositionList private val mTargetView: WeakReference? = builder.mTargetView - private val mBubbleShowCaseListener: BubbleShowCaseListener? = builder.mBubbleShowCaseListener + private val mBubbleShowCaseListener: BubbleShowCaseListener? = builder.mBubbleShowCaseListener //Sequence params - private val mSequenceListener: SequenceShowCaseListener? = builder.mSequenceShowCaseListener - private val isFirstOfSequence: Boolean = builder.mIsFirstOfSequence!! - private val isLastOfSequence: Boolean = builder.mIsLastOfSequence!! + private val mSequenceListener: SequenceShowCaseListener? = builder.mSequenceShowCaseListener + private val isFirstOfSequence: Boolean = builder.mIsFirstOfSequence!! + private val isLastOfSequence: Boolean = builder.mIsLastOfSequence!! //References private var backgroundDimLayout: RelativeLayout? = null private var bubbleMessageViewBuilder: BubbleMessageView.Builder? = null - fun show(){ - if(mShowOnce != null){ - if(isBubbleShowCaseHasBeenShowedPreviously(mShowOnce)){ + fun show() { + if (mShowOnce != null) { + if (isBubbleShowCaseHasBeenShowedPreviously(mShowOnce)) { notifyDismissToSequenceListener() return - } else{ + } else { registerBubbleShowCaseInPreferences(mShowOnce) } } @@ -99,14 +96,24 @@ class BubbleShowCase(builder: BubbleShowCaseBuilder){ handler.postDelayed({ val target = mTargetView.get()!! //If the arrow list is empty, the arrow position is set by default depending on the targetView position on the screen - if(mArrowPositionList.isEmpty()){ - if(ScreenUtils.isViewLocatedAtHalfTopOfTheScreen(mActivity.get()!!, target)) mArrowPositionList.add(ArrowPosition.TOP) else mArrowPositionList.add(ArrowPosition.BOTTOM) + if (mArrowPositionList.isEmpty()) { + if (ScreenUtils.isViewLocatedAtHalfTopOfTheScreen( + mActivity.get()!!, + target + ) + ) mArrowPositionList.add(ArrowPosition.TOP) else mArrowPositionList.add( + ArrowPosition.BOTTOM + ) bubbleMessageViewBuilder = getBubbleMessageViewBuilder() } if (isVisibleOnScreen(target)) { addTargetViewAtBackgroundDimLayout(target, backgroundDimLayout) - addBubbleMessageViewDependingOnTargetView(target, bubbleMessageViewBuilder!!, backgroundDimLayout) + addBubbleMessageViewDependingOnTargetView( + target, + bubbleMessageViewBuilder!!, + backgroundDimLayout + ) } else { dismiss() } @@ -114,10 +121,17 @@ class BubbleShowCase(builder: BubbleShowCaseBuilder){ } else { addBubbleMessageViewOnScreenCenter(bubbleMessageViewBuilder!!, backgroundDimLayout) } - if(isFirstOfSequence){ + if (isFirstOfSequence) { //Add the background dim layout above the root view val animation = AnimationUtils.getFadeInAnimation(0, DURATION_BACKGROUND_ANIMATION) - backgroundDimLayout?.let { rootView.addView(AnimationUtils.setAnimationToView(backgroundDimLayout!!, animation)) } + backgroundDimLayout?.let { + rootView.addView( + AnimationUtils.setAnimationToView( + backgroundDimLayout!!, + animation + ) + ) + } } } @@ -138,7 +152,7 @@ class BubbleShowCase(builder: BubbleShowCaseBuilder){ backgroundDimLayout = null } - private fun notifyDismissToSequenceListener(){ + private fun notifyDismissToSequenceListener() { mSequenceListener?.let { mSequenceListener.onDismiss() } } @@ -148,51 +162,59 @@ class BubbleShowCase(builder: BubbleShowCaseBuilder){ } private fun getBackgroundDimLayout(): RelativeLayout { - if(mActivity.get()!!.findViewById(FOREGROUND_LAYOUT_ID) != null) + if (mActivity.get()!!.findViewById(FOREGROUND_LAYOUT_ID) != null) return mActivity.get()!!.findViewById(FOREGROUND_LAYOUT_ID) val backgroundLayout = RelativeLayout(mActivity.get()!!) backgroundLayout.id = FOREGROUND_LAYOUT_ID - backgroundLayout.layoutParams = RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) - backgroundLayout.setBackgroundColor(ContextCompat.getColor(mActivity.get()!!, R.color.transparent_grey)) + backgroundLayout.layoutParams = RelativeLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + backgroundLayout.setBackgroundColor( + ContextCompat.getColor( + mActivity.get()!!, + R.color.transparent_grey + ) + ) backgroundLayout.isClickable = true return backgroundLayout } - private fun setBackgroundDimListener(backgroundDimLayout: RelativeLayout?){ + private fun setBackgroundDimListener(backgroundDimLayout: RelativeLayout?) { backgroundDimLayout?.setOnClickListener { mBubbleShowCaseListener?.onBackgroundDimClick(this) } } - private fun getBubbleMessageViewBuilder(): BubbleMessageView.Builder{ + private fun getBubbleMessageViewBuilder(): BubbleMessageView.Builder { return BubbleMessageView.Builder() - .from(mActivity.get()!!) - .arrowPosition(mArrowPositionList) - .backgroundColor(mBackgroundColor) - .textColor(mTextColor) - .titleTextSize(mTitleTextSize) - .subtitleTextSize(mSubtitleTextSize) - .title(mTitle) - .subtitle(mSubtitle) - .image(mImage) - .closeActionImage(mCloseAction) - .disableCloseAction(mDisableCloseAction) - .listener(object : OnBubbleMessageViewListener { - override fun onBubbleClick() { - mBubbleShowCaseListener?.onBubbleClick(this@BubbleShowCase) - } - - override fun onCloseActionImageClick() { - dismiss() - mBubbleShowCaseListener?.onCloseActionImageClick(this@BubbleShowCase) - } - }) + .from(mActivity.get()!!) + .arrowPosition(mArrowPositionList) + .backgroundColor(mBackgroundColor) + .textColor(mTextColor) + .titleTextSize(mTitleTextSize) + .subtitleTextSize(mSubtitleTextSize) + .title(mTitle) + .subtitle(mSubtitle) + .image(mImage) + .closeActionImage(mCloseAction) + .disableCloseAction(mDisableCloseAction) + .listener(object : OnBubbleMessageViewListener { + override fun onBubbleClick() { + mBubbleShowCaseListener?.onBubbleClick(this@BubbleShowCase) + } + + override fun onCloseActionImageClick() { + dismiss() + mBubbleShowCaseListener?.onCloseActionImageClick(this@BubbleShowCase) + } + }) } - private fun isBubbleShowCaseHasBeenShowedPreviously(id: String): Boolean{ + private fun isBubbleShowCaseHasBeenShowedPreviously(id: String): Boolean { val mPrefs = mActivity.get()!!.getSharedPreferences(SHARED_PREFS_NAME, MODE_PRIVATE) - return getString(mPrefs, id)!=null + return getString(mPrefs, id) != null } - private fun registerBubbleShowCaseInPreferences(id: String){ + private fun registerBubbleShowCaseInPreferences(id: String) { val mPrefs = mActivity.get()!!.getSharedPreferences(SHARED_PREFS_NAME, MODE_PRIVATE) setString(mPrefs, id, id) } @@ -211,140 +233,206 @@ class BubbleShowCase(builder: BubbleShowCaseBuilder){ /** * This function takes a screenshot of the targetView, creating an ImageView from it. This new ImageView is also set on the layout passed by param */ - private fun addTargetViewAtBackgroundDimLayout(targetView: View?, backgroundDimLayout: RelativeLayout?) { - if(targetView==null) return + private fun addTargetViewAtBackgroundDimLayout( + targetView: View?, + backgroundDimLayout: RelativeLayout? + ) { + if (targetView == null) return val targetScreenshot = takeScreenshot(targetView, mHighlightMode) val targetScreenshotView = ImageView(mActivity.get()!!) targetScreenshotView.setImageBitmap(targetScreenshot) targetScreenshotView.setOnClickListener { - if(!mDisableTargetClick) + if (!mDisableTargetClick) dismiss() mBubbleShowCaseListener?.onTargetClick(this) } - val targetViewParams = RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT) - targetViewParams.setMargins(getXposition(targetView), getYposition(targetView), getScreenWidth(mActivity.get()!!) - (getXposition(targetView) + targetView.width), 0) - backgroundDimLayout?.addView(AnimationUtils.setBouncingAnimation(targetScreenshotView, 0, DURATION_BEATING_ANIMATION), targetViewParams) + val targetViewParams = RelativeLayout.LayoutParams( + RelativeLayout.LayoutParams.WRAP_CONTENT, + RelativeLayout.LayoutParams.WRAP_CONTENT + ) + targetViewParams.setMargins( + getXposition(targetView), + getYposition(targetView), + getScreenWidth(mActivity.get()!!) - (getXposition(targetView) + targetView.width), + 0 + ) + backgroundDimLayout?.addView( + AnimationUtils.setBouncingAnimation( + targetScreenshotView, + 0, + DURATION_BEATING_ANIMATION + ), targetViewParams + ) } /** * This function creates the BubbleMessageView depending the position of the target and the desired arrow position. This new view is also set on the layout passed by param */ - private fun addBubbleMessageViewDependingOnTargetView(targetView: View?, bubbleMessageViewBuilder: BubbleMessageView.Builder, backgroundDimLayout: RelativeLayout?) { - if(targetView==null) return - val showCaseParams = RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT) + private fun addBubbleMessageViewDependingOnTargetView( + targetView: View?, + bubbleMessageViewBuilder: BubbleMessageView.Builder, + backgroundDimLayout: RelativeLayout? + ) { + if (targetView == null) return + val showCaseParams = RelativeLayout.LayoutParams( + RelativeLayout.LayoutParams.MATCH_PARENT, + RelativeLayout.LayoutParams.WRAP_CONTENT + ) when (bubbleMessageViewBuilder.mArrowPosition[0]) { ArrowPosition.LEFT -> { showCaseParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT) - if(ScreenUtils.isViewLocatedAtHalfTopOfTheScreen(mActivity.get()!!, targetView)){ + if (ScreenUtils.isViewLocatedAtHalfTopOfTheScreen(mActivity.get()!!, targetView)) { showCaseParams.setMargins( - getXposition(targetView) + targetView.width, - getYposition(targetView), - if(isTablet()) getScreenWidth(mActivity.get()!!) - (getXposition(targetView) + targetView.width) - getMessageViewWidthOnTablet(getScreenWidth(mActivity.get()!!) - (getXposition(targetView) + targetView.width)) else 0, - 0) + getXposition(targetView) + targetView.width, + getYposition(targetView), + if (isTablet()) getScreenWidth(mActivity.get()!!) - (getXposition(targetView) + targetView.width) - getMessageViewWidthOnTablet( + getScreenWidth(mActivity.get()!!) - (getXposition(targetView) + targetView.width) + ) else 0, + 0 + ) showCaseParams.addRule(RelativeLayout.ALIGN_PARENT_TOP) - } else{ + } else { showCaseParams.setMargins( - getXposition(targetView) + targetView.width, - 0, - if(isTablet()) getScreenWidth(mActivity.get()!!) - (getXposition(targetView) + targetView.width) - getMessageViewWidthOnTablet(getScreenWidth(mActivity.get()!!) - (getXposition(targetView) + targetView.width)) else 0, - getScreenHeight(mActivity.get()!!) - getYposition(targetView) - targetView.height) + getXposition(targetView) + targetView.width, + 0, + if (isTablet()) getScreenWidth(mActivity.get()!!) - (getXposition(targetView) + targetView.width) - getMessageViewWidthOnTablet( + getScreenWidth(mActivity.get()!!) - (getXposition(targetView) + targetView.width) + ) else 0, + getScreenHeight(mActivity.get()!!) - getYposition(targetView) - targetView.height + ) showCaseParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM) } } ArrowPosition.RIGHT -> { showCaseParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT) - if(ScreenUtils.isViewLocatedAtHalfTopOfTheScreen(mActivity.get()!!, targetView)){ + if (ScreenUtils.isViewLocatedAtHalfTopOfTheScreen(mActivity.get()!!, targetView)) { showCaseParams.setMargins( - if(isTablet()) getXposition(targetView) - getMessageViewWidthOnTablet(getXposition(targetView)) else 0, - getYposition(targetView), - getScreenWidth(mActivity.get()!!) - getXposition(targetView), - 0) + if (isTablet()) getXposition(targetView) - getMessageViewWidthOnTablet( + getXposition(targetView) + ) else 0, + getYposition(targetView), + getScreenWidth(mActivity.get()!!) - getXposition(targetView), + 0 + ) showCaseParams.addRule(RelativeLayout.ALIGN_PARENT_TOP) - } else{ + } else { showCaseParams.setMargins( - if(isTablet()) getXposition(targetView) - getMessageViewWidthOnTablet(getXposition(targetView)) else 0, - 0, - getScreenWidth(mActivity.get()!!) - getXposition(targetView), - getScreenHeight(mActivity.get()!!) - getYposition(targetView) - targetView.height) + if (isTablet()) getXposition(targetView) - getMessageViewWidthOnTablet( + getXposition(targetView) + ) else 0, + 0, + getScreenWidth(mActivity.get()!!) - getXposition(targetView), + getScreenHeight(mActivity.get()!!) - getYposition(targetView) - targetView.height + ) showCaseParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM) } } ArrowPosition.TOP -> { showCaseParams.addRule(RelativeLayout.ALIGN_PARENT_TOP) - if(ScreenUtils.isViewLocatedAtHalfLeftOfTheScreen(mActivity.get()!!, targetView)){ + if (ScreenUtils.isViewLocatedAtHalfLeftOfTheScreen(mActivity.get()!!, targetView)) { showCaseParams.setMargins( - if (isTablet()) getXposition(targetView) else 0, - getYposition(targetView) + targetView.height, - if (isTablet()) getScreenWidth(mActivity.get()!!) - getXposition(targetView) - getMessageViewWidthOnTablet(getScreenWidth(mActivity.get()!!) - getXposition(targetView)) else 0, - 0) - } else{ + if (isTablet()) getXposition(targetView) else 0, + getYposition(targetView) + targetView.height, + if (isTablet()) getScreenWidth(mActivity.get()!!) - getXposition(targetView) - getMessageViewWidthOnTablet( + getScreenWidth(mActivity.get()!!) - getXposition(targetView) + ) else 0, + 0 + ) + } else { showCaseParams.setMargins( - if (isTablet()) getXposition(targetView) + targetView.width - getMessageViewWidthOnTablet(getXposition(targetView)) else 0, - getYposition(targetView) + targetView.height, - if (isTablet()) getScreenWidth(mActivity.get()!!) - getXposition(targetView) - targetView.width else 0, - 0) + if (isTablet()) getXposition(targetView) + targetView.width - getMessageViewWidthOnTablet( + getXposition(targetView) + ) else 0, + getYposition(targetView) + targetView.height, + if (isTablet()) getScreenWidth(mActivity.get()!!) - getXposition(targetView) - targetView.width else 0, + 0 + ) } } ArrowPosition.BOTTOM -> { showCaseParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM) - if(ScreenUtils.isViewLocatedAtHalfLeftOfTheScreen(mActivity.get()!!, targetView)){ + if (ScreenUtils.isViewLocatedAtHalfLeftOfTheScreen(mActivity.get()!!, targetView)) { showCaseParams.setMargins( - if (isTablet()) getXposition(targetView) else 0, - 0, - if (isTablet()) getScreenWidth(mActivity.get()!!) - getXposition(targetView) - getMessageViewWidthOnTablet(getScreenWidth(mActivity.get()!!) - getXposition(targetView)) else 0, - getScreenHeight(mActivity.get()!!) - getYposition(targetView)) + if (isTablet()) getXposition(targetView) else 0, + 0, + if (isTablet()) getScreenWidth(mActivity.get()!!) - getXposition(targetView) - getMessageViewWidthOnTablet( + getScreenWidth(mActivity.get()!!) - getXposition(targetView) + ) else 0, + getScreenHeight(mActivity.get()!!) - getYposition(targetView) + ) } else { showCaseParams.setMargins( - if (isTablet()) getXposition(targetView) + targetView.width - getMessageViewWidthOnTablet(getXposition(targetView)) else 0, - 0, - if (isTablet()) getScreenWidth(mActivity.get()!!) - getXposition(targetView) - targetView.width else 0, - getScreenHeight(mActivity.get()!!) - getYposition(targetView)) + if (isTablet()) getXposition(targetView) + targetView.width - getMessageViewWidthOnTablet( + getXposition(targetView) + ) else 0, + 0, + if (isTablet()) getScreenWidth(mActivity.get()!!) - getXposition(targetView) - targetView.width else 0, + getScreenHeight(mActivity.get()!!) - getYposition(targetView) + ) } } } - val bubbleMessageView = bubbleMessageViewBuilder.targetViewScreenLocation(RectF( + val bubbleMessageView = bubbleMessageViewBuilder.targetViewScreenLocation( + RectF( getXposition(targetView).toFloat(), getYposition(targetView).toFloat(), getXposition(targetView).toFloat() + targetView.width, - getYposition(targetView).toFloat() + targetView.height)) - .build() + getYposition(targetView).toFloat() + targetView.height + ) + ) + .build() bubbleMessageView.id = createViewId() val animation = AnimationUtils.getScaleAnimation(0, DURATION_SHOW_CASE_ANIMATION) - backgroundDimLayout?.addView(AnimationUtils.setAnimationToView(bubbleMessageView, animation), showCaseParams) + backgroundDimLayout?.addView( + AnimationUtils.setAnimationToView( + bubbleMessageView, + animation + ), showCaseParams + ) } /** * This function creates a BubbleMessageView and it is set on the center of the layout passed by param */ - private fun addBubbleMessageViewOnScreenCenter(bubbleMessageViewBuilder: BubbleMessageView.Builder, backgroundDimLayout: RelativeLayout?) { - val showCaseParams = RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT) - showCaseParams.addRule(RelativeLayout. CENTER_VERTICAL) + private fun addBubbleMessageViewOnScreenCenter( + bubbleMessageViewBuilder: BubbleMessageView.Builder, + backgroundDimLayout: RelativeLayout? + ) { + val showCaseParams = RelativeLayout.LayoutParams( + RelativeLayout.LayoutParams.MATCH_PARENT, + RelativeLayout.LayoutParams.WRAP_CONTENT + ) + showCaseParams.addRule(RelativeLayout.CENTER_VERTICAL) val bubbleMessageView: BubbleMessageView = bubbleMessageViewBuilder.build() bubbleMessageView.id = createViewId() - if(isTablet()) showCaseParams.setMargins( - if (isTablet()) getScreenWidth(mActivity.get()!!)/2 - ScreenUtils.dpToPx(MAX_WIDTH_MESSAGE_VIEW_TABLET)/2 else 0, - 0, - if (isTablet()) getScreenWidth(mActivity.get()!!)/2 - ScreenUtils.dpToPx(MAX_WIDTH_MESSAGE_VIEW_TABLET)/2 else 0, - 0) + if (isTablet()) showCaseParams.setMargins( + if (isTablet()) getScreenWidth(mActivity.get()!!) / 2 - ScreenUtils.dpToPx( + MAX_WIDTH_MESSAGE_VIEW_TABLET + ) / 2 else 0, + 0, + if (isTablet()) getScreenWidth(mActivity.get()!!) / 2 - ScreenUtils.dpToPx( + MAX_WIDTH_MESSAGE_VIEW_TABLET + ) / 2 else 0, + 0 + ) val animation = AnimationUtils.getScaleAnimation(0, DURATION_SHOW_CASE_ANIMATION) - backgroundDimLayout?.addView(AnimationUtils.setAnimationToView(bubbleMessageView, animation), showCaseParams) + backgroundDimLayout?.addView( + AnimationUtils.setAnimationToView( + bubbleMessageView, + animation + ), showCaseParams + ) } - private fun createViewId(): Int { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - View.generateViewId() - } else { - System.currentTimeMillis().toInt() / 1000 - } - } + private fun createViewId(): Int = View.generateViewId() private fun takeScreenshot(targetView: View, highlightMode: HighlightMode?): Bitmap? { - if (highlightMode==null || highlightMode == HighlightMode.VIEW_LAYOUT) + if (highlightMode == null || highlightMode == HighlightMode.VIEW_LAYOUT) return takeScreenshotOfLayoutView(targetView) return takeScreenshotOfSurfaceView(targetView) } @@ -358,7 +446,13 @@ class BubbleShowCase(builder: BubbleShowCaseBuilder){ val currentScreenView = rootView.getChildAt(0) currentScreenView.buildDrawingCache() val bitmap: Bitmap - bitmap = Bitmap.createBitmap(currentScreenView.drawingCache, getXposition(targetView), getYposition(targetView), targetView.width, targetView.height) + bitmap = Bitmap.createBitmap( + currentScreenView.drawingCache, + getXposition(targetView), + getYposition(targetView), + targetView.width, + targetView.height + ) currentScreenView.isDrawingCacheEnabled = false currentScreenView.destroyDrawingCache() return bitmap @@ -376,40 +470,46 @@ class BubbleShowCase(builder: BubbleShowCaseBuilder){ } private fun isVisibleOnScreen(targetView: View?): Boolean { - if(targetView!=null){ - if(getXposition(targetView) >= 0 && getYposition(targetView) >= 0){ + if (targetView != null) { + if (getXposition(targetView) >= 0 && getYposition(targetView) >= 0) { return getXposition(targetView) != 0 || getYposition(targetView) != 0 } } return false } - private fun getXposition(targetView: View): Int{ + private fun getXposition(targetView: View): Int { return ScreenUtils.getAxisXpositionOfViewOnScreen(targetView) - getScreenHorizontalOffset() } - private fun getYposition(targetView: View): Int{ + private fun getYposition(targetView: View): Int { return ScreenUtils.getAxisYpositionOfViewOnScreen(targetView) - getScreenVerticalOffset() } - private fun getScreenHeight(context: Context): Int{ + private fun getScreenHeight(context: Context): Int { return ScreenUtils.getScreenHeight(context) - getScreenVerticalOffset() } - private fun getScreenWidth(context: Context): Int{ + private fun getScreenWidth(context: Context): Int { return ScreenUtils.getScreenWidth(context) - getScreenHorizontalOffset() } - private fun getScreenVerticalOffset(): Int{ - return if(backgroundDimLayout !=null) ScreenUtils.getAxisYpositionOfViewOnScreen(backgroundDimLayout!!) else 0 + private fun getScreenVerticalOffset(): Int { + return if (backgroundDimLayout != null) ScreenUtils.getAxisYpositionOfViewOnScreen( + backgroundDimLayout!! + ) else 0 } - private fun getScreenHorizontalOffset(): Int{ - return if(backgroundDimLayout !=null) ScreenUtils.getAxisXpositionOfViewOnScreen(backgroundDimLayout!!) else 0 + private fun getScreenHorizontalOffset(): Int { + return if (backgroundDimLayout != null) ScreenUtils.getAxisXpositionOfViewOnScreen( + backgroundDimLayout!! + ) else 0 } - private fun getMessageViewWidthOnTablet(availableSpace: Int): Int{ - return if(availableSpace > ScreenUtils.dpToPx(MAX_WIDTH_MESSAGE_VIEW_TABLET)) ScreenUtils.dpToPx(MAX_WIDTH_MESSAGE_VIEW_TABLET) else availableSpace + private fun getMessageViewWidthOnTablet(availableSpace: Int): Int { + return if (availableSpace > ScreenUtils.dpToPx(MAX_WIDTH_MESSAGE_VIEW_TABLET)) ScreenUtils.dpToPx( + MAX_WIDTH_MESSAGE_VIEW_TABLET + ) else availableSpace } private fun isTablet(): Boolean = mActivity.get()!!.resources.getBoolean(R.bool.isTablet) diff --git a/bubbleshowcase/src/main/java/com/elconfidencial/bubbleshowcase/BubbleShowCaseBuilder.kt b/bubbleshowcase/src/main/java/com/elconfidencial/bubbleshowcase/BubbleShowCaseBuilder.kt index a0dc459..c205c08 100644 --- a/bubbleshowcase/src/main/java/com/elconfidencial/bubbleshowcase/BubbleShowCaseBuilder.kt +++ b/bubbleshowcase/src/main/java/com/elconfidencial/bubbleshowcase/BubbleShowCaseBuilder.kt @@ -2,16 +2,16 @@ package com.elconfidencial.bubbleshowcase import android.app.Activity import android.graphics.drawable.Drawable -import android.support.v4.content.ContextCompat import android.view.View import android.view.ViewTreeObserver +import androidx.core.content.ContextCompat import java.lang.ref.WeakReference -import java.util.ArrayList +import java.util.* /** * Created by jcampos on 04/09/2018. */ -class BubbleShowCaseBuilder{ +class BubbleShowCaseBuilder { internal var mActivity: WeakReference? = null internal var mImage: Drawable? = null @@ -38,7 +38,7 @@ class BubbleShowCaseBuilder{ /** * Builder constructor. It needs an instance of the current activity to convert it to a weak reference in order to avoid memory leaks */ - constructor(activity: Activity){ + constructor(activity: Activity) { mActivity = WeakReference(activity) } @@ -72,7 +72,7 @@ class BubbleShowCaseBuilder{ * - If this param is not passed, the BubbleShowCase will not have main image */ fun imageResourceId(resId: Int): BubbleShowCaseBuilder { - mImage = ContextCompat.getDrawable(mActivity!!.get(), resId) + mImage = mActivity?.get()?.let { ContextCompat.getDrawable(it, resId) } return this } @@ -90,7 +90,7 @@ class BubbleShowCaseBuilder{ * - If this param is not defined, a default close icon is displayed */ fun closeActionImageResourceId(resId: Int): BubbleShowCaseBuilder { - mCloseAction = ContextCompat.getDrawable(mActivity!!.get(), resId) + mCloseAction = mActivity?.get()?.let { ContextCompat.getDrawable(it, resId) } return this } @@ -109,7 +109,7 @@ class BubbleShowCaseBuilder{ * - #3F51B5 color will be set if this param is not defined */ fun backgroundColorResourceId(colorResId: Int): BubbleShowCaseBuilder { - mBackgroundColor = ContextCompat.getColor(mActivity!!.get(), colorResId) + mBackgroundColor = mActivity?.get()?.let { ContextCompat.getColor(it, colorResId) } return this } @@ -127,7 +127,7 @@ class BubbleShowCaseBuilder{ * - White color will be set if this param is not defined */ fun textColorResourceId(colorResId: Int): BubbleShowCaseBuilder { - mTextColor = ContextCompat.getColor(mActivity!!.get(), colorResId) + mTextColor = mActivity?.get()?.let { ContextCompat.getColor(it, colorResId) } return this } @@ -171,7 +171,7 @@ class BubbleShowCaseBuilder{ * If this variable is true, when user clicks on the target, the showcase will not be dismissed * Default value -> false */ - fun disableTargetClick(isDisabled: Boolean): BubbleShowCaseBuilder{ + fun disableTargetClick(isDisabled: Boolean): BubbleShowCaseBuilder { mDisableTargetClick = isDisabled return this } @@ -180,7 +180,7 @@ class BubbleShowCaseBuilder{ * If this variable is true, close action button will be gone * Default value -> false */ - fun disableCloseAction(isDisabled: Boolean): BubbleShowCaseBuilder{ + fun disableCloseAction(isDisabled: Boolean): BubbleShowCaseBuilder { mDisableCloseAction = isDisabled return this } @@ -235,12 +235,12 @@ class BubbleShowCaseBuilder{ return this } - internal fun isFirstOfSequence(isFirst: Boolean): BubbleShowCaseBuilder{ + internal fun isFirstOfSequence(isFirst: Boolean): BubbleShowCaseBuilder { mIsFirstOfSequence = isFirst return this } - internal fun isLastOfSequence(isLast: Boolean): BubbleShowCaseBuilder{ + internal fun isLastOfSequence(isLast: Boolean): BubbleShowCaseBuilder { mIsLastOfSequence = isLast return this } @@ -249,9 +249,9 @@ class BubbleShowCaseBuilder{ * Build the BubbleShowCase object from the builder one */ private fun build(): BubbleShowCase { - if(mIsFirstOfSequence ==null) + if (mIsFirstOfSequence == null) mIsFirstOfSequence = true - if(mIsLastOfSequence ==null) + if (mIsLastOfSequence == null) mIsLastOfSequence = true return BubbleShowCase(this) @@ -260,7 +260,7 @@ class BubbleShowCaseBuilder{ /** * Show the BubbleShowCase using the params added previously */ - fun show(): BubbleShowCase{ + fun show(): BubbleShowCase { val bubbleShowCase = build() if (mTargetView != null) { val targetView = mTargetView!!.get() @@ -268,9 +268,13 @@ class BubbleShowCaseBuilder{ //If the view is not already painted, we wait for it waiting for view changes using OnGlobalLayoutListener onGlobalLayoutListenerTargetView = ViewTreeObserver.OnGlobalLayoutListener { bubbleShowCase.show() - targetView.viewTreeObserver.removeOnGlobalLayoutListener(onGlobalLayoutListenerTargetView) + targetView.viewTreeObserver.removeOnGlobalLayoutListener( + onGlobalLayoutListenerTargetView + ) } - targetView.viewTreeObserver.addOnGlobalLayoutListener(onGlobalLayoutListenerTargetView) + targetView.viewTreeObserver.addOnGlobalLayoutListener( + onGlobalLayoutListenerTargetView + ) } else { bubbleShowCase.show() } diff --git a/bubbleshowcase/src/main/java/com/elconfidencial/bubbleshowcase/BubbleShowCaseSequence.kt b/bubbleshowcase/src/main/java/com/elconfidencial/bubbleshowcase/BubbleShowCaseSequence.kt index f86b45f..f7a23e7 100644 --- a/bubbleshowcase/src/main/java/com/elconfidencial/bubbleshowcase/BubbleShowCaseSequence.kt +++ b/bubbleshowcase/src/main/java/com/elconfidencial/bubbleshowcase/BubbleShowCaseSequence.kt @@ -3,10 +3,10 @@ package com.elconfidencial.bubbleshowcase /** * Created by jcampos on 10/09/2018. */ -class BubbleShowCaseSequence{ +class BubbleShowCaseSequence { private val mBubbleShowCaseBuilderList = ArrayList() - init{ + init { mBubbleShowCaseBuilderList.clear() } @@ -22,16 +22,16 @@ class BubbleShowCaseSequence{ fun show() = show(0) - private fun show(position: Int){ - if(position >= mBubbleShowCaseBuilderList.size) + private fun show(position: Int) { + if (position >= mBubbleShowCaseBuilderList.size) return - when(position){ + when (position) { 0 -> { mBubbleShowCaseBuilderList[position].isFirstOfSequence(true) mBubbleShowCaseBuilderList[position].isLastOfSequence(false) } - mBubbleShowCaseBuilderList.size-1 -> { + mBubbleShowCaseBuilderList.size - 1 -> { mBubbleShowCaseBuilderList[position].isFirstOfSequence(false) mBubbleShowCaseBuilderList[position].isLastOfSequence(true) } @@ -40,7 +40,7 @@ class BubbleShowCaseSequence{ mBubbleShowCaseBuilderList[position].isLastOfSequence(false) } } - mBubbleShowCaseBuilderList[position].sequenceListener(object : SequenceShowCaseListener{ + mBubbleShowCaseBuilderList[position].sequenceListener(object : SequenceShowCaseListener { override fun onDismiss() { show(position + 1) } diff --git a/bubbleshowcase/src/main/java/com/elconfidencial/bubbleshowcase/OnBubbleMessageViewListener.kt b/bubbleshowcase/src/main/java/com/elconfidencial/bubbleshowcase/OnBubbleMessageViewListener.kt index 043f045..1273cc0 100644 --- a/bubbleshowcase/src/main/java/com/elconfidencial/bubbleshowcase/OnBubbleMessageViewListener.kt +++ b/bubbleshowcase/src/main/java/com/elconfidencial/bubbleshowcase/OnBubbleMessageViewListener.kt @@ -9,7 +9,6 @@ interface OnBubbleMessageViewListener { */ fun onCloseActionImageClick() - /** * It is called when a user clicks the BubbleMessageView */ diff --git a/bubbleshowcase/src/main/java/com/elconfidencial/bubbleshowcase/ScreenUtils.kt b/bubbleshowcase/src/main/java/com/elconfidencial/bubbleshowcase/ScreenUtils.kt index bd2a472..48848b5 100644 --- a/bubbleshowcase/src/main/java/com/elconfidencial/bubbleshowcase/ScreenUtils.kt +++ b/bubbleshowcase/src/main/java/com/elconfidencial/bubbleshowcase/ScreenUtils.kt @@ -4,14 +4,11 @@ import android.app.Activity import android.content.Context import android.content.res.Resources import android.graphics.Point -import android.graphics.Rect import android.util.DisplayMetrics import android.view.View import android.view.ViewGroup import android.view.WindowManager -import android.view.Window.ID_ANDROID_CONTENT - - +import kotlin.math.roundToInt /** * Created by jcampos on 05/09/2018. @@ -72,12 +69,12 @@ object ScreenUtils { fun pxToDp(px: Int): Int { val metrics = Resources.getSystem().displayMetrics - return Math.round(px / (metrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)) + return (px / (metrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)).roundToInt() } fun dpToPx(dp: Int): Int { val metrics = Resources.getSystem().displayMetrics - return Math.round(dp * (metrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)) + return (dp * (metrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)).roundToInt() } fun isViewLocatedAtHalfTopOfTheScreen(activity: Activity, targetView: View): Boolean{ diff --git a/bubbleshowcase/src/main/res/drawable/rounded_rectangle.xml b/bubbleshowcase/src/main/res/drawable/rounded_rectangle.xml index b452d87..3d7f654 100644 --- a/bubbleshowcase/src/main/res/drawable/rounded_rectangle.xml +++ b/bubbleshowcase/src/main/res/drawable/rounded_rectangle.xml @@ -1,11 +1,11 @@ - - + android:padding="10dp" + android:shape="rectangle"> + + android:topRightRadius="2dp" /> \ No newline at end of file diff --git a/bubbleshowcase/src/main/res/layout/view_bubble_message.xml b/bubbleshowcase/src/main/res/layout/view_bubble_message.xml index a437e22..2c5cf49 100644 --- a/bubbleshowcase/src/main/res/layout/view_bubble_message.xml +++ b/bubbleshowcase/src/main/res/layout/view_bubble_message.xml @@ -1,15 +1,15 @@ - + android:paddingEnd="20dp" + android:paddingBottom="36dp"> @@ -40,12 +40,13 @@ android:id="@+id/textViewShowCaseTitle" android:layout_width="match_parent" android:layout_height="wrap_content" - android:text="Title" android:breakStrategy="simple" android:textColor="@color/white" - android:visibility="gone" android:textSize="16sp" - android:textStyle="bold" /> + android:textStyle="bold" + android:visibility="gone" + tools:targetApi="m" + tools:text="Title" /> + android:textSize="14sp" + android:visibility="gone" + tools:targetApi="m" /> - - \ No newline at end of file + \ No newline at end of file diff --git a/build.gradle b/build.gradle index f88a8d6..9632da1 100644 --- a/build.gradle +++ b/build.gradle @@ -1,20 +1,16 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.2.61' - ext.dokka_version = '0.9.17' + ext.kotlin_version = '1.4.20' repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.0.1' + classpath 'com.android.tools.build:gradle:4.1.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - - //Maven plugins classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4' classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' - classpath "org.jetbrains.dokka:dokka-gradle-plugin:${dokka_version}" } } diff --git a/gradle.properties b/gradle.properties index f1c49f3..1e0c1d3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,6 +9,8 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. +android.enableJetifier=true +android.useAndroidX=true org.gradle.jvmargs=-Xmx1536m # When configured, Gradle will run in incubating parallel mode. diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cae88ba..7656aed 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-4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip