mirror of
				https://codeberg.org/tom79/Fedilab.git
				synced 2025-10-20 11:20:16 +03:00 
			
		
		
		
	Fix issue #745 - Update library for bottom buttons.
This commit is contained in:
		
							parent
							
								
									d70c285bea
								
							
						
					
					
						commit
						107ac13e15
					
				
					 21 changed files with 865 additions and 35 deletions
				
			
		|  | @ -78,7 +78,7 @@ allprojects { | ||||||
| } | } | ||||||
| dependencies { | dependencies { | ||||||
|     implementation project(':autoimageslider') |     implementation project(':autoimageslider') | ||||||
|     implementation 'androidx.appcompat:appcompat:1.5.1' |     implementation 'androidx.appcompat:appcompat:1.6.0' | ||||||
| 
 | 
 | ||||||
|     implementation 'com.google.android.material:material:1.7.0' |     implementation 'com.google.android.material:material:1.7.0' | ||||||
| 
 | 
 | ||||||
|  | @ -94,7 +94,6 @@ dependencies { | ||||||
|     implementation "org.conscrypt:conscrypt-android:2.5.2" |     implementation "org.conscrypt:conscrypt-android:2.5.2" | ||||||
|     implementation 'com.vanniktech:emoji-one:0.6.0' |     implementation 'com.vanniktech:emoji-one:0.6.0' | ||||||
|     implementation 'com.github.GrenderG:Toasty:1.5.2' |     implementation 'com.github.GrenderG:Toasty:1.5.2' | ||||||
|     implementation 'org.framagit.tom79:SparkButton:1.0.13' |  | ||||||
|     implementation "com.github.bumptech.glide:glide:4.14.2" |     implementation "com.github.bumptech.glide:glide:4.14.2" | ||||||
|     implementation "com.github.bumptech.glide:okhttp3-integration:4.14.2" |     implementation "com.github.bumptech.glide:okhttp3-integration:4.14.2" | ||||||
|     implementation("com.github.bumptech.glide:recyclerview-integration:4.14.2") { |     implementation("com.github.bumptech.glide:recyclerview-integration:4.14.2") { | ||||||
|  | @ -107,7 +106,7 @@ dependencies { | ||||||
|     implementation 'com.github.mergehez:ArgPlayer:v3.1' |     implementation 'com.github.mergehez:ArgPlayer:v3.1' | ||||||
|     implementation project(path: ':mytransl') |     implementation project(path: ':mytransl') | ||||||
|     implementation project(path: ':ratethisapp') |     implementation project(path: ':ratethisapp') | ||||||
| 
 |     implementation project(path: ':sparkbutton') | ||||||
| 
 | 
 | ||||||
|     implementation 'com.burhanrashid52:photoeditor:1.5.1' |     implementation 'com.burhanrashid52:photoeditor:1.5.1' | ||||||
|     implementation("com.vanniktech:android-image-cropper:4.3.3") |     implementation("com.vanniktech:android-image-cropper:4.3.3") | ||||||
|  |  | ||||||
|  | @ -638,21 +638,13 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> | ||||||
| 
 | 
 | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         holder.binding.actionButtonFavorite.pressOnTouch(false); | 
 | ||||||
|         holder.binding.actionButtonBoost.pressOnTouch(false); |  | ||||||
|         holder.binding.actionButtonBookmark.pressOnTouch(false); |  | ||||||
|         holder.binding.actionButtonFavorite.setActiveImage(R.drawable.ic_round_star_24); |         holder.binding.actionButtonFavorite.setActiveImage(R.drawable.ic_round_star_24); | ||||||
|         holder.binding.actionButtonFavorite.setInactiveImage(R.drawable.ic_round_star_border_24); |         holder.binding.actionButtonFavorite.setInactiveImage(R.drawable.ic_round_star_border_24); | ||||||
|         holder.binding.actionButtonBookmark.setActiveImage(R.drawable.ic_round_bookmark_24); |         holder.binding.actionButtonBookmark.setActiveImage(R.drawable.ic_round_bookmark_24); | ||||||
|         holder.binding.actionButtonBookmark.setInactiveImage(R.drawable.ic_round_bookmark_border_24); |         holder.binding.actionButtonBookmark.setInactiveImage(R.drawable.ic_round_bookmark_border_24); | ||||||
|         holder.binding.actionButtonBoost.setActiveImage(R.drawable.ic_round_repeat_24); |         holder.binding.actionButtonBoost.setActiveImage(R.drawable.ic_round_repeat_24); | ||||||
|         holder.binding.actionButtonBoost.setInactiveImage(R.drawable.ic_round_repeat_24); |         holder.binding.actionButtonBoost.setInactiveImage(R.drawable.ic_round_repeat_24); | ||||||
|         holder.binding.actionButtonFavorite.setDisableCircle(true); |  | ||||||
|         holder.binding.actionButtonBoost.setDisableCircle(true); |  | ||||||
|         holder.binding.actionButtonBookmark.setDisableCircle(true); |  | ||||||
|         holder.binding.actionButtonFavorite.setActiveImageTint(R.color.marked_icon); |  | ||||||
|         holder.binding.actionButtonBoost.setActiveImageTint(R.color.boost_icon); |  | ||||||
|         holder.binding.actionButtonBookmark.setActiveImageTint(R.color.marked_icon); |  | ||||||
|         applyColor(context, holder); |         applyColor(context, holder); | ||||||
| 
 | 
 | ||||||
|         if (status.pinned) { |         if (status.pinned) { | ||||||
|  | @ -2319,9 +2311,6 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> | ||||||
|             Helper.changeDrawableColor(context, R.drawable.ic_bot, theme_icons_color); |             Helper.changeDrawableColor(context, R.drawable.ic_bot, theme_icons_color); | ||||||
|             Helper.changeDrawableColor(context, R.drawable.ic_round_reply_24, theme_icons_color); |             Helper.changeDrawableColor(context, R.drawable.ic_round_reply_24, theme_icons_color); | ||||||
|             Helper.changeDrawableColor(context, holder.binding.actionButtonTranslate, theme_icons_color); |             Helper.changeDrawableColor(context, holder.binding.actionButtonTranslate, theme_icons_color); | ||||||
|             holder.binding.actionButtonFavorite.setInActiveImageTintColor(theme_icons_color); |  | ||||||
|             holder.binding.actionButtonBookmark.setInActiveImageTintColor(theme_icons_color); |  | ||||||
|             holder.binding.actionButtonBoost.setInActiveImageTintColor(theme_icons_color); |  | ||||||
|             holder.binding.replyCount.setTextColor(theme_icons_color); |             holder.binding.replyCount.setTextColor(theme_icons_color); | ||||||
|         } |         } | ||||||
|         if (theme_statuses_color != -1) { |         if (theme_statuses_color != -1) { | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" | <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|     android:width="24dp" |     android:width="24dp" | ||||||
|     android:height="24dp" |     android:height="24dp" | ||||||
|     android:tint="?attr/colorControlNormal" |     android:tint="@color/marked_icon" | ||||||
|     android:viewportWidth="24" |     android:viewportWidth="24" | ||||||
|     android:viewportHeight="24"> |     android:viewportHeight="24"> | ||||||
|     <path |     <path | ||||||
|  |  | ||||||
							
								
								
									
										10
									
								
								app/src/main/res/drawable/ic_round_repeat_active_24.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								app/src/main/res/drawable/ic_round_repeat_active_24.xml
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | ||||||
|  | <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|  |     android:width="24dp" | ||||||
|  |     android:height="24dp" | ||||||
|  |     android:tint="@color/boost_icon" | ||||||
|  |     android:viewportWidth="24" | ||||||
|  |     android:viewportHeight="24"> | ||||||
|  |     <path | ||||||
|  |         android:fillColor="?attr/colorControlNormal" | ||||||
|  |         android:pathData="M7,7h10v1.79c0,0.45 0.54,0.67 0.85,0.35l2.79,-2.79c0.2,-0.2 0.2,-0.51 0,-0.71l-2.79,-2.79c-0.31,-0.31 -0.85,-0.09 -0.85,0.36L17,5L6,5c-0.55,0 -1,0.45 -1,1v4c0,0.55 0.45,1 1,1s1,-0.45 1,-1L7,7zM17,17L7,17v-1.79c0,-0.45 -0.54,-0.67 -0.85,-0.35l-2.79,2.79c-0.2,0.2 -0.2,0.51 0,0.71l2.79,2.79c0.31,0.31 0.85,0.09 0.85,-0.36L7,19h11c0.55,0 1,-0.45 1,-1v-4c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1v3z" /> | ||||||
|  | </vector> | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" | <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|     android:width="24dp" |     android:width="24dp" | ||||||
|     android:height="24dp" |     android:height="24dp" | ||||||
|     android:tint="?attr/colorControlNormal" |     android:tint="@color/marked_icon" | ||||||
|     android:viewportWidth="24" |     android:viewportWidth="24" | ||||||
|     android:viewportHeight="24"> |     android:viewportHeight="24"> | ||||||
|     <path |     <path | ||||||
|  |  | ||||||
|  | @ -17,6 +17,7 @@ | ||||||
| <com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android" | <com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" |     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||||
|     xmlns:tools="http://schemas.android.com/tools" |     xmlns:tools="http://schemas.android.com/tools" | ||||||
|  |     xmlns:sparkbutton="http://schemas.android.com/apk/res-auto" | ||||||
|     android:id="@+id/cardview_container" |     android:id="@+id/cardview_container" | ||||||
|     android:layout_width="match_parent" |     android:layout_width="match_parent" | ||||||
|     android:layout_height="wrap_content" |     android:layout_height="wrap_content" | ||||||
|  | @ -655,12 +656,11 @@ | ||||||
|                 android:layout_height="48dp" |                 android:layout_height="48dp" | ||||||
|                 android:adjustViewBounds="true" |                 android:adjustViewBounds="true" | ||||||
|                 android:contentDescription="@string/reblog_add" |                 android:contentDescription="@string/reblog_add" | ||||||
|                 app:sparkbutton_activeImage="@drawable/ic_round_repeat_24" |                 app:activeImage="@drawable/ic_round_repeat_active_24" | ||||||
|                 app:sparkbutton_animationSpeed="1.5" |                 app:iconSize="28dp" | ||||||
|                 app:sparkbutton_iconSize="24dp" |                 app:inactiveImage="@drawable/ic_round_repeat_24" | ||||||
|                 app:sparkbutton_inActiveImage="@drawable/ic_round_repeat_24" |                 app:primaryColor="@color/boost_icon" | ||||||
|                 app:sparkbutton_primaryColor="@color/boost_icon" |                 app:secondaryColor="@color/boost_icon" /> | ||||||
|                 app:sparkbutton_secondaryColor="@color/boost_icon" /> |  | ||||||
| 
 | 
 | ||||||
|             <androidx.appcompat.widget.AppCompatImageButton |             <androidx.appcompat.widget.AppCompatImageButton | ||||||
|                 android:id="@+id/action_button_quote" |                 android:id="@+id/action_button_quote" | ||||||
|  | @ -691,12 +691,12 @@ | ||||||
|                 android:layout_gravity="center" |                 android:layout_gravity="center" | ||||||
|                 android:adjustViewBounds="true" |                 android:adjustViewBounds="true" | ||||||
|                 android:contentDescription="@string/favourite_add" |                 android:contentDescription="@string/favourite_add" | ||||||
|                 app:sparkbutton_activeImage="@drawable/ic_round_star_24" |                 app:activeImage="@drawable/ic_round_star_24" | ||||||
|                 app:sparkbutton_animationSpeed="1.5" |                 app:animationSpeed="1.5" | ||||||
|                 app:sparkbutton_iconSize="24dp" |                 app:inactiveImage="@drawable/ic_round_star_border_24" | ||||||
|                 app:sparkbutton_inActiveImage="@drawable/ic_round_star_border_24" |                 app:primaryColor="@color/marked_icon" | ||||||
|                 app:sparkbutton_primaryColor="@color/marked_icon" |                 app:secondaryColor="@color/marked_icon" | ||||||
|                 app:sparkbutton_secondaryColor="@color/marked_icon" /> |                 sparkbutton:iconSize="28dp" /> | ||||||
| 
 | 
 | ||||||
|             <com.varunest.sparkbutton.SparkButton |             <com.varunest.sparkbutton.SparkButton | ||||||
|                 android:id="@+id/action_button_bookmark" |                 android:id="@+id/action_button_bookmark" | ||||||
|  | @ -709,12 +709,12 @@ | ||||||
|                 android:layout_gravity="center" |                 android:layout_gravity="center" | ||||||
|                 android:adjustViewBounds="true" |                 android:adjustViewBounds="true" | ||||||
|                 android:contentDescription="@string/bookmark_add" |                 android:contentDescription="@string/bookmark_add" | ||||||
|                 app:sparkbutton_activeImage="@drawable/ic_round_bookmark_24" |                 app:activeImage="@drawable/ic_round_bookmark_24" | ||||||
|                 app:sparkbutton_animationSpeed="1.5" |                 app:animationSpeed="1.5" | ||||||
|                 app:sparkbutton_iconSize="24dp" |                 app:inactiveImage="@drawable/ic_round_bookmark_border_24" | ||||||
|                 app:sparkbutton_inActiveImage="@drawable/ic_round_bookmark_border_24" |                 app:primaryColor="@color/marked_icon" | ||||||
|                 app:sparkbutton_primaryColor="@color/marked_icon" |                 app:secondaryColor="@color/marked_icon" | ||||||
|                 app:sparkbutton_secondaryColor="@color/marked_icon" /> |                 sparkbutton:iconSize="28dp" /> | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|             <androidx.appcompat.widget.AppCompatImageButton |             <androidx.appcompat.widget.AppCompatImageButton | ||||||
|  |  | ||||||
|  | @ -3,3 +3,4 @@ include ':app' | ||||||
| include ':autoimageslider' | include ':autoimageslider' | ||||||
| include ':mytransl' | include ':mytransl' | ||||||
| include ':ratethisapp' | include ':ratethisapp' | ||||||
|  | include ':sparkbutton' | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								sparkbutton/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								sparkbutton/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | /build | ||||||
							
								
								
									
										27
									
								
								sparkbutton/build.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								sparkbutton/build.gradle
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | ||||||
|  | apply plugin: 'com.android.library' | ||||||
|  | 
 | ||||||
|  | group = 'com.github.tom79' | ||||||
|  | 
 | ||||||
|  | android { | ||||||
|  |     compileSdkVersion 33 | ||||||
|  | 
 | ||||||
|  |     defaultConfig { | ||||||
|  |         minSdkVersion 15 | ||||||
|  |         targetSdkVersion 33 | ||||||
|  |         versionCode 3 | ||||||
|  |         versionName "1.0.12" | ||||||
|  |     } | ||||||
|  |     buildTypes { | ||||||
|  |         release { | ||||||
|  |             minifyEnabled false | ||||||
|  |             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     lintOptions { | ||||||
|  |         abortOnError false | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | dependencies { | ||||||
|  |     implementation fileTree(dir: 'libs', include: ['*.jar']) | ||||||
|  |     implementation 'androidx.appcompat:appcompat:1.6.0' | ||||||
|  | } | ||||||
							
								
								
									
										3
									
								
								sparkbutton/gradle.properties
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								sparkbutton/gradle.properties
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | ||||||
|  | POM_NAME=SparkButton | ||||||
|  | POM_ARTIFACT_ID=sparkbutton | ||||||
|  | POM_PACKAGING=aar | ||||||
							
								
								
									
										96
									
								
								sparkbutton/maven-push.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								sparkbutton/maven-push.gradle
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,96 @@ | ||||||
|  | apply plugin: 'maven' | ||||||
|  | apply plugin: 'signing' | ||||||
|  | 
 | ||||||
|  | def isReleaseBuild() { | ||||||
|  |     return VERSION_NAME.contains("SNAPSHOT") == false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | def getReleaseRepositoryUrl() { | ||||||
|  |     return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL | ||||||
|  |             : "https://oss.sonatype.org/service/local/staging/deploy/maven2/" | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | def getSnapshotRepositoryUrl() { | ||||||
|  |     return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL | ||||||
|  |             : "https://oss.sonatype.org/content/repositories/snapshots/" | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | def getRepositoryUsername() { | ||||||
|  |     return hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : "" | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | def getRepositoryPassword() { | ||||||
|  |     return hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : "" | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | afterEvaluate { project -> | ||||||
|  |     uploadArchives { | ||||||
|  |         repositories { | ||||||
|  |             mavenDeployer { | ||||||
|  |                 beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } | ||||||
|  | 
 | ||||||
|  |                 pom.groupId = GROUP | ||||||
|  |                 pom.artifactId = POM_ARTIFACT_ID | ||||||
|  |                 pom.version = VERSION_NAME | ||||||
|  | 
 | ||||||
|  |                 repository(url: getReleaseRepositoryUrl()) { | ||||||
|  |                     authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) | ||||||
|  |                 } | ||||||
|  |                 snapshotRepository(url: getSnapshotRepositoryUrl()) { | ||||||
|  |                     authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 pom.project { | ||||||
|  |                     name POM_NAME | ||||||
|  |                     packaging POM_PACKAGING | ||||||
|  |                     description POM_DESCRIPTION | ||||||
|  |                     url POM_URL | ||||||
|  | 
 | ||||||
|  |                     scm { | ||||||
|  |                         url POM_SCM_URL | ||||||
|  |                         connection POM_SCM_CONNECTION | ||||||
|  |                         developerConnection POM_SCM_DEV_CONNECTION | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     licenses { | ||||||
|  |                         license { | ||||||
|  |                             name POM_LICENCE_NAME | ||||||
|  |                             url POM_LICENCE_URL | ||||||
|  |                             distribution POM_LICENCE_DIST | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     developers { | ||||||
|  |                         developer { | ||||||
|  |                             id POM_DEVELOPER_ID | ||||||
|  |                             name POM_DEVELOPER_NAME | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     signing { | ||||||
|  |         required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") } | ||||||
|  |         sign configurations.archives | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     //task androidJavadocs(type: Javadoc) { | ||||||
|  |     //source = android.sourceSets.main.allJava | ||||||
|  |     //} | ||||||
|  | 
 | ||||||
|  |     //task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { | ||||||
|  |     //classifier = 'javadoc' | ||||||
|  |     //from androidJavadocs.destinationDir | ||||||
|  |     //} | ||||||
|  | 
 | ||||||
|  |     task androidSourcesJar(type: Jar) { | ||||||
|  |         classifier = 'sources' | ||||||
|  |         from android.sourceSets.main.java.sourceFiles | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     artifacts { | ||||||
|  |         archives androidSourcesJar | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										17
									
								
								sparkbutton/proguard-rules.pro
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								sparkbutton/proguard-rules.pro
									
									
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,17 @@ | ||||||
|  | # Add project specific ProGuard rules here. | ||||||
|  | # By default, the flags in this file are appended to flags specified | ||||||
|  | # in /android-sdk/tools/proguard/proguard-android.txt | ||||||
|  | # You can edit the include path and order by changing the proguardFiles | ||||||
|  | # directive in build.gradle. | ||||||
|  | # | ||||||
|  | # For more details, see | ||||||
|  | #   http://developer.android.com/guide/developing/tools/proguard.html | ||||||
|  | 
 | ||||||
|  | # Add any project specific keep options here: | ||||||
|  | 
 | ||||||
|  | # If your project uses WebView with JS, uncomment the following | ||||||
|  | # and specify the fully qualified class name to the JavaScript interface | ||||||
|  | # class: | ||||||
|  | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { | ||||||
|  | #   public *; | ||||||
|  | #} | ||||||
							
								
								
									
										4
									
								
								sparkbutton/src/main/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								sparkbutton/src/main/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,4 @@ | ||||||
|  | <manifest package="com.varunest.sparkbutton"> | ||||||
|  | 
 | ||||||
|  |     <application /> | ||||||
|  | </manifest> | ||||||
|  | @ -0,0 +1,313 @@ | ||||||
|  | package com.varunest.sparkbutton; | ||||||
|  | 
 | ||||||
|  | import android.animation.Animator; | ||||||
|  | import android.animation.AnimatorListenerAdapter; | ||||||
|  | import android.animation.AnimatorSet; | ||||||
|  | import android.animation.ObjectAnimator; | ||||||
|  | import android.annotation.TargetApi; | ||||||
|  | import android.content.Context; | ||||||
|  | import android.content.res.TypedArray; | ||||||
|  | import android.os.Build; | ||||||
|  | import android.util.AttributeSet; | ||||||
|  | import android.view.Gravity; | ||||||
|  | import android.view.MotionEvent; | ||||||
|  | import android.view.View; | ||||||
|  | import android.view.animation.AccelerateDecelerateInterpolator; | ||||||
|  | import android.view.animation.DecelerateInterpolator; | ||||||
|  | import android.view.animation.OvershootInterpolator; | ||||||
|  | import android.widget.FrameLayout; | ||||||
|  | import android.widget.ImageView; | ||||||
|  | 
 | ||||||
|  | import androidx.annotation.ColorInt; | ||||||
|  | import androidx.annotation.DrawableRes; | ||||||
|  | import androidx.annotation.Px; | ||||||
|  | import androidx.appcompat.widget.AppCompatImageView; | ||||||
|  | import androidx.core.content.ContextCompat; | ||||||
|  | 
 | ||||||
|  | import com.varunest.sparkbutton.helpers.SparkAnimationView; | ||||||
|  | import com.varunest.sparkbutton.helpers.Utils; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * @author varun 7th July 2016 | ||||||
|  |  */ | ||||||
|  | public class SparkButton extends FrameLayout implements View.OnClickListener { | ||||||
|  |     private static final DecelerateInterpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator(); | ||||||
|  |     private static final AccelerateDecelerateInterpolator ACCELERATE_DECELERATE_INTERPOLATOR = new AccelerateDecelerateInterpolator(); | ||||||
|  |     private static final OvershootInterpolator OVERSHOOT_INTERPOLATOR = new OvershootInterpolator(4); | ||||||
|  | 
 | ||||||
|  |     private static final int INVALID_RESOURCE_ID = -1; | ||||||
|  |     private static final float ANIMATIONVIEW_SIZE_FACTOR = 3; | ||||||
|  |     private static final float DOTS_SIZE_FACTOR = .08f; | ||||||
|  |     int activeImageTint; | ||||||
|  |     int inActiveImageTint; | ||||||
|  |     private @DrawableRes | ||||||
|  |     int imageResourceIdActive = INVALID_RESOURCE_ID; | ||||||
|  |     private @DrawableRes | ||||||
|  |     int imageResourceIdInactive = INVALID_RESOURCE_ID; | ||||||
|  |     private @Px | ||||||
|  |     int imageSize; | ||||||
|  |     private @ColorInt | ||||||
|  |     int primaryColor; | ||||||
|  |     private @ColorInt | ||||||
|  |     int secondaryColor; | ||||||
|  |     private SparkAnimationView sparkAnimationView; | ||||||
|  |     private ImageView imageView; | ||||||
|  |     private float animationSpeed = 1; | ||||||
|  |     private boolean isChecked = false; | ||||||
|  |     private AnimatorSet animatorSet; | ||||||
|  |     private SparkEventListener listener; | ||||||
|  | 
 | ||||||
|  |     SparkButton(Context context) { | ||||||
|  |         super(context); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public SparkButton(Context context, AttributeSet attrs) { | ||||||
|  |         super(context, attrs); | ||||||
|  |         initFromXML(attrs); | ||||||
|  |         init(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public SparkButton(Context context, AttributeSet attrs, int defStyleAttr) { | ||||||
|  |         super(context, attrs, defStyleAttr); | ||||||
|  |         initFromXML(attrs); | ||||||
|  |         init(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @TargetApi(Build.VERSION_CODES.LOLLIPOP) | ||||||
|  |     public SparkButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { | ||||||
|  |         super(context, attrs, defStyleAttr, defStyleRes); | ||||||
|  |         initFromXML(attrs); | ||||||
|  |         init(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     void init() { | ||||||
|  |         int animationViewSize = (int) (imageSize * ANIMATIONVIEW_SIZE_FACTOR); | ||||||
|  | 
 | ||||||
|  |         sparkAnimationView = new SparkAnimationView(getContext()); | ||||||
|  |         LayoutParams dotsViewLayoutParams = new LayoutParams(animationViewSize, animationViewSize, Gravity.CENTER); | ||||||
|  |         sparkAnimationView.setLayoutParams(dotsViewLayoutParams); | ||||||
|  | 
 | ||||||
|  |         sparkAnimationView.setColors(secondaryColor, primaryColor); | ||||||
|  |         sparkAnimationView.setMaxDotSize((int) (imageSize * DOTS_SIZE_FACTOR)); | ||||||
|  | 
 | ||||||
|  |         addView(sparkAnimationView); | ||||||
|  | 
 | ||||||
|  |         imageView = new AppCompatImageView(getContext()); | ||||||
|  |         LayoutParams imageViewLayoutParams = new LayoutParams(imageSize, imageSize, Gravity.CENTER); | ||||||
|  |         imageView.setLayoutParams(imageViewLayoutParams); | ||||||
|  | 
 | ||||||
|  |         addView(imageView); | ||||||
|  | 
 | ||||||
|  |         if (imageResourceIdInactive != INVALID_RESOURCE_ID) { | ||||||
|  |             // should load inactive img first | ||||||
|  |             imageView.setImageResource(imageResourceIdInactive); | ||||||
|  |         } else if (imageResourceIdActive != INVALID_RESOURCE_ID) { | ||||||
|  |             imageView.setImageResource(imageResourceIdActive); | ||||||
|  |         } else { | ||||||
|  |             throw new IllegalArgumentException("One of Inactive/Active Image Resources is required!"); | ||||||
|  |         } | ||||||
|  |         setOnTouchListener(); | ||||||
|  |         setOnClickListener(this); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Call this function to start spark animation | ||||||
|  |      */ | ||||||
|  |     public void playAnimation() { | ||||||
|  |         if (animatorSet != null) { | ||||||
|  |             animatorSet.cancel(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         imageView.animate().cancel(); | ||||||
|  |         imageView.setScaleX(0); | ||||||
|  |         imageView.setScaleY(0); | ||||||
|  |         sparkAnimationView.setInnerCircleRadiusProgress(0); | ||||||
|  |         sparkAnimationView.setOuterCircleRadiusProgress(0); | ||||||
|  |         sparkAnimationView.setCurrentProgress(0); | ||||||
|  | 
 | ||||||
|  |         animatorSet = new AnimatorSet(); | ||||||
|  | 
 | ||||||
|  |         ObjectAnimator outerCircleAnimator = ObjectAnimator.ofFloat(sparkAnimationView, SparkAnimationView.OUTER_CIRCLE_RADIUS_PROGRESS, 0.1f, 1f); | ||||||
|  |         outerCircleAnimator.setDuration((long) (250 / animationSpeed)); | ||||||
|  |         outerCircleAnimator.setInterpolator(DECELERATE_INTERPOLATOR); | ||||||
|  | 
 | ||||||
|  |         ObjectAnimator innerCircleAnimator = ObjectAnimator.ofFloat(sparkAnimationView, SparkAnimationView.INNER_CIRCLE_RADIUS_PROGRESS, 0.1f, 1f); | ||||||
|  |         innerCircleAnimator.setDuration((long) (200 / animationSpeed)); | ||||||
|  |         innerCircleAnimator.setStartDelay((long) (200 / animationSpeed)); | ||||||
|  |         innerCircleAnimator.setInterpolator(DECELERATE_INTERPOLATOR); | ||||||
|  | 
 | ||||||
|  |         ObjectAnimator starScaleYAnimator = ObjectAnimator.ofFloat(imageView, ImageView.SCALE_Y, 0.2f, 1f); | ||||||
|  |         starScaleYAnimator.setDuration((long) (350 / animationSpeed)); | ||||||
|  |         starScaleYAnimator.setStartDelay((long) (250 / animationSpeed)); | ||||||
|  |         starScaleYAnimator.setInterpolator(OVERSHOOT_INTERPOLATOR); | ||||||
|  | 
 | ||||||
|  |         ObjectAnimator starScaleXAnimator = ObjectAnimator.ofFloat(imageView, ImageView.SCALE_X, 0.2f, 1f); | ||||||
|  |         starScaleXAnimator.setDuration((long) (350 / animationSpeed)); | ||||||
|  |         starScaleXAnimator.setStartDelay((long) (250 / animationSpeed)); | ||||||
|  |         starScaleXAnimator.setInterpolator(OVERSHOOT_INTERPOLATOR); | ||||||
|  | 
 | ||||||
|  |         ObjectAnimator dotsAnimator = ObjectAnimator.ofFloat(sparkAnimationView, SparkAnimationView.DOTS_PROGRESS, 0, 1f); | ||||||
|  |         dotsAnimator.setDuration((long) (900 / animationSpeed)); | ||||||
|  |         dotsAnimator.setStartDelay((long) (50 / animationSpeed)); | ||||||
|  |         dotsAnimator.setInterpolator(ACCELERATE_DECELERATE_INTERPOLATOR); | ||||||
|  | 
 | ||||||
|  |         animatorSet.playTogether( | ||||||
|  |                 outerCircleAnimator, | ||||||
|  |                 innerCircleAnimator, | ||||||
|  |                 starScaleYAnimator, | ||||||
|  |                 starScaleXAnimator, | ||||||
|  |                 dotsAnimator | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         animatorSet.addListener(new AnimatorListenerAdapter() { | ||||||
|  |             @Override | ||||||
|  |             public void onAnimationCancel(Animator animation) { | ||||||
|  |                 sparkAnimationView.setInnerCircleRadiusProgress(0); | ||||||
|  |                 sparkAnimationView.setOuterCircleRadiusProgress(0); | ||||||
|  |                 sparkAnimationView.setCurrentProgress(0); | ||||||
|  |                 imageView.setScaleX(1); | ||||||
|  |                 imageView.setScaleY(1); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             @Override | ||||||
|  |             public void onAnimationEnd(Animator animation) { | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             @Override | ||||||
|  |             public void onAnimationStart(Animator animation) { | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         animatorSet.start(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     public @Px | ||||||
|  |     int getImageSize() { | ||||||
|  |         return imageSize; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setImageSize(@Px int imageSize) { | ||||||
|  |         this.imageSize = imageSize; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public @ColorInt | ||||||
|  |     int getPrimaryColor() { | ||||||
|  |         return primaryColor; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setPrimaryColor(@ColorInt int primaryColor) { | ||||||
|  |         this.primaryColor = primaryColor; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public @ColorInt | ||||||
|  |     int getSecondaryColor() { | ||||||
|  |         return secondaryColor; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setSecondaryColor(@ColorInt int secondaryColor) { | ||||||
|  |         this.secondaryColor = secondaryColor; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setAnimationSpeed(float animationSpeed) { | ||||||
|  |         this.animationSpeed = animationSpeed; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @return Returns whether the button is checked (Active) or not. | ||||||
|  |      */ | ||||||
|  |     public boolean isChecked() { | ||||||
|  |         return isChecked; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Change Button State (Works only if both active and disabled image resource is defined) | ||||||
|  |      * | ||||||
|  |      * @param flag desired checked state of the button | ||||||
|  |      */ | ||||||
|  |     public void setChecked(boolean flag) { | ||||||
|  |         isChecked = flag; | ||||||
|  |         imageView.setImageResource(isChecked ? imageResourceIdActive : imageResourceIdInactive); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setInactiveImage(int inactiveResource) { | ||||||
|  |         this.imageResourceIdInactive = inactiveResource; | ||||||
|  |         imageView.setImageResource(isChecked ? imageResourceIdActive : imageResourceIdInactive); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setActiveImage(int activeResource) { | ||||||
|  |         this.imageResourceIdActive = activeResource; | ||||||
|  |         imageView.setImageResource(isChecked ? imageResourceIdActive : imageResourceIdInactive); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public void onClick(View v) { | ||||||
|  |         boolean shouldPlayAnimation = listener == null || listener.onEvent(this, isChecked); | ||||||
|  | 
 | ||||||
|  |         if (shouldPlayAnimation) { | ||||||
|  |             if (imageResourceIdInactive != INVALID_RESOURCE_ID) { | ||||||
|  |                 isChecked = !isChecked; | ||||||
|  | 
 | ||||||
|  |                 imageView.setImageResource(isChecked ? imageResourceIdActive : imageResourceIdInactive); | ||||||
|  | 
 | ||||||
|  |                 if (animatorSet != null) { | ||||||
|  |                     animatorSet.cancel(); | ||||||
|  |                 } | ||||||
|  |                 if (isChecked) { | ||||||
|  |                     sparkAnimationView.setVisibility(VISIBLE); | ||||||
|  |                     playAnimation(); | ||||||
|  |                 } else { | ||||||
|  |                     sparkAnimationView.setVisibility(INVISIBLE); | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 playAnimation(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     private void setOnTouchListener() { | ||||||
|  |         setOnTouchListener((v, event) -> { | ||||||
|  |             switch (event.getAction()) { | ||||||
|  |                 case MotionEvent.ACTION_DOWN: | ||||||
|  |                     imageView.animate().scaleX(0.8f).scaleY(0.8f).setDuration(150).setInterpolator(DECELERATE_INTERPOLATOR); | ||||||
|  |                     setPressed(true); | ||||||
|  |                     break; | ||||||
|  | 
 | ||||||
|  |                 case MotionEvent.ACTION_MOVE: | ||||||
|  |                     break; | ||||||
|  | 
 | ||||||
|  |                 case MotionEvent.ACTION_UP: | ||||||
|  |                     imageView.animate().scaleX(1).scaleY(1).setInterpolator(DECELERATE_INTERPOLATOR); | ||||||
|  |                     if (isPressed()) { | ||||||
|  |                         performClick(); | ||||||
|  |                         setPressed(false); | ||||||
|  |                     } | ||||||
|  |                     break; | ||||||
|  | 
 | ||||||
|  |                 case MotionEvent.ACTION_CANCEL: | ||||||
|  |                     imageView.animate().scaleX(1).scaleY(1).setInterpolator(DECELERATE_INTERPOLATOR); | ||||||
|  |                     break; | ||||||
|  |             } | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private int getColor(int id) { | ||||||
|  |         return ContextCompat.getColor(getContext(), id); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     private void initFromXML(AttributeSet attr) { | ||||||
|  |         TypedArray a = getContext().obtainStyledAttributes(attr, R.styleable.SparkButton); | ||||||
|  |         imageSize = a.getDimensionPixelOffset(R.styleable.SparkButton_iconSize, Utils.dpToPx(getContext(), 50)); | ||||||
|  |         imageResourceIdActive = a.getResourceId(R.styleable.SparkButton_activeImage, INVALID_RESOURCE_ID); | ||||||
|  |         imageResourceIdInactive = a.getResourceId(R.styleable.SparkButton_inactiveImage, INVALID_RESOURCE_ID); | ||||||
|  |         primaryColor = ContextCompat.getColor(getContext(), a.getResourceId(R.styleable.SparkButton_primaryColor, R.color.spark_primary_color)); | ||||||
|  |         secondaryColor = ContextCompat.getColor(getContext(), a.getResourceId(R.styleable.SparkButton_secondaryColor, R.color.spark_secondary_color)); | ||||||
|  |         animationSpeed = a.getFloat(R.styleable.SparkButton_animationSpeed, 1); | ||||||
|  |         // recycle typedArray | ||||||
|  |         a.recycle(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,61 @@ | ||||||
|  | package com.varunest.sparkbutton; | ||||||
|  | 
 | ||||||
|  | import android.content.Context; | ||||||
|  | 
 | ||||||
|  | import androidx.annotation.ColorInt; | ||||||
|  | import androidx.annotation.DrawableRes; | ||||||
|  | 
 | ||||||
|  | import com.varunest.sparkbutton.helpers.Utils; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * @author varun on 07/07/16. | ||||||
|  |  */ | ||||||
|  | public class SparkButtonBuilder { | ||||||
|  |     private final SparkButton sparkButton; | ||||||
|  |     private final Context context; | ||||||
|  | 
 | ||||||
|  |     public SparkButtonBuilder(Context context) { | ||||||
|  |         this.context = context; | ||||||
|  |         sparkButton = new SparkButton(context); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public SparkButtonBuilder setActiveImage(@DrawableRes int resourceId) { | ||||||
|  |         sparkButton.setActiveImage(resourceId); | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public SparkButtonBuilder setInactiveImage(@DrawableRes int resourceId) { | ||||||
|  |         sparkButton.setInactiveImage(resourceId); | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public SparkButtonBuilder setPrimaryColor(@ColorInt int color) { | ||||||
|  |         sparkButton.setPrimaryColor(color); | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public SparkButtonBuilder setSecondaryColor(int color) { | ||||||
|  |         sparkButton.setSecondaryColor(color); | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public SparkButtonBuilder setImageSizePx(int px) { | ||||||
|  |         sparkButton.setImageSize(px); | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public SparkButtonBuilder setImageSizeDp(int dp) { | ||||||
|  |         sparkButton.setImageSize(Utils.dpToPx(context, dp)); | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public SparkButtonBuilder setAnimationSpeed(float speed) { | ||||||
|  |         sparkButton.setAnimationSpeed(speed); | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public SparkButton build() { | ||||||
|  |         sparkButton.init(); | ||||||
|  |         return sparkButton; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,10 @@ | ||||||
|  | package com.varunest.sparkbutton; | ||||||
|  | 
 | ||||||
|  | import androidx.annotation.NonNull; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * @author varun on 07/07/16. | ||||||
|  |  */ | ||||||
|  | public interface SparkEventListener { | ||||||
|  |     boolean onEvent(@NonNull SparkButton button, boolean buttonState); | ||||||
|  | } | ||||||
|  | @ -0,0 +1,249 @@ | ||||||
|  | package com.varunest.sparkbutton.helpers; | ||||||
|  | 
 | ||||||
|  | import android.animation.ArgbEvaluator; | ||||||
|  | import android.annotation.TargetApi; | ||||||
|  | import android.content.Context; | ||||||
|  | import android.graphics.Canvas; | ||||||
|  | import android.graphics.Paint; | ||||||
|  | import android.graphics.PorterDuff; | ||||||
|  | import android.graphics.PorterDuffXfermode; | ||||||
|  | import android.os.Build; | ||||||
|  | import android.util.AttributeSet; | ||||||
|  | import android.util.Property; | ||||||
|  | import android.view.View; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | public class SparkAnimationView extends View { | ||||||
|  |     public static final Property<SparkAnimationView, Float> INNER_CIRCLE_RADIUS_PROGRESS = | ||||||
|  |             new Property<SparkAnimationView, Float>(Float.class, "innerCircleRadiusProgress") { | ||||||
|  |                 @Override | ||||||
|  |                 public Float get(SparkAnimationView object) { | ||||||
|  |                     return object.getInnerCircleRadiusProgress(); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 @Override | ||||||
|  |                 public void set(SparkAnimationView object, Float value) { | ||||||
|  |                     object.setInnerCircleRadiusProgress(value); | ||||||
|  |                 } | ||||||
|  |             }; | ||||||
|  |     private static final int DOTS_COUNT = 12; | ||||||
|  |     private static final int OUTER_DOTS_POSITION_ANGLE = 360 / DOTS_COUNT; | ||||||
|  |     private static final ArgbEvaluator argbEvaluator = new ArgbEvaluator(); | ||||||
|  |     public static final Property<SparkAnimationView, Float> DOTS_PROGRESS = new Property<SparkAnimationView, Float>(Float.class, "dotsProgress") { | ||||||
|  |         @Override | ||||||
|  |         public Float get(SparkAnimationView object) { | ||||||
|  |             return object.getCurrentProgress(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         @Override | ||||||
|  |         public void set(SparkAnimationView object, Float value) { | ||||||
|  |             object.setCurrentProgress(value); | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |     public static final Property<SparkAnimationView, Float> OUTER_CIRCLE_RADIUS_PROGRESS = | ||||||
|  |             new Property<SparkAnimationView, Float>(Float.class, "outerCircleRadiusProgress") { | ||||||
|  |                 @Override | ||||||
|  |                 public Float get(SparkAnimationView object) { | ||||||
|  |                     return object.getOuterCircleRadiusProgress(); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 @Override | ||||||
|  |                 public void set(SparkAnimationView object, Float value) { | ||||||
|  |                     object.setOuterCircleRadiusProgress(value); | ||||||
|  |                 } | ||||||
|  |             }; | ||||||
|  |     private final Paint[] dotsPaints = new Paint[4]; | ||||||
|  |     private final Paint circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG); | ||||||
|  |     private final Paint maskPaint = new Paint(Paint.ANTI_ALIAS_FLAG); | ||||||
|  |     private int primaryColor = 0xFFFFC107; | ||||||
|  |     private int primaryColorDark = 0xFFFF9800; | ||||||
|  |     private int secondaryColor = 0xFFFF5722; | ||||||
|  |     private int secondaryColorDark = 0xFFF44336; | ||||||
|  |     private int centerX; | ||||||
|  |     private int centerY; | ||||||
|  |     private float maxOuterDotsRadius; | ||||||
|  |     private float maxInnerDotsRadius; | ||||||
|  |     private float maxDotSize; | ||||||
|  |     private float currentProgress = 0; | ||||||
|  |     private float currentRadius1 = 0; | ||||||
|  |     private float currentDotSize1 = 0; | ||||||
|  |     private float currentDotSize2 = 0; | ||||||
|  |     private float currentRadius2 = 0; | ||||||
|  |     private float outerCircleRadiusProgress = 0f; | ||||||
|  |     private float innerCircleRadiusProgress = 0f; | ||||||
|  |     private float maxCircleSize; | ||||||
|  | 
 | ||||||
|  |     public SparkAnimationView(Context context) { | ||||||
|  |         super(context); | ||||||
|  |         init(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public SparkAnimationView(Context context, AttributeSet attrs) { | ||||||
|  |         super(context, attrs); | ||||||
|  |         init(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public SparkAnimationView(Context context, AttributeSet attrs, int defStyleAttr) { | ||||||
|  |         super(context, attrs, defStyleAttr); | ||||||
|  |         init(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @TargetApi(Build.VERSION_CODES.LOLLIPOP) | ||||||
|  |     public SparkAnimationView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { | ||||||
|  |         super(context, attrs, defStyleAttr, defStyleRes); | ||||||
|  |         init(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void init() { | ||||||
|  |         setLayerType(View.LAYER_TYPE_HARDWARE, null); | ||||||
|  | 
 | ||||||
|  |         maxDotSize = Utils.dpToPx(getContext(), 4); | ||||||
|  |         for (int i = 0; i < dotsPaints.length; i++) { | ||||||
|  |             dotsPaints[i] = new Paint(Paint.ANTI_ALIAS_FLAG); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     protected void onSizeChanged(int w, int h, int oldw, int oldh) { | ||||||
|  |         centerX = w / 2; | ||||||
|  |         centerY = h / 2; | ||||||
|  |         maxOuterDotsRadius = w / 2 - maxDotSize * 2; | ||||||
|  |         maxInnerDotsRadius = 0.8f * maxOuterDotsRadius; | ||||||
|  |         maxCircleSize = w / 4.3f; | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     protected void onDraw(Canvas canvas) { | ||||||
|  |         drawOuterDotsFrame(canvas); | ||||||
|  |         drawInnerDotsFrame(canvas); | ||||||
|  | 
 | ||||||
|  |         canvas.drawCircle(getWidth() / 2, getHeight() / 2, outerCircleRadiusProgress * maxCircleSize, circlePaint); | ||||||
|  |         canvas.drawCircle(getWidth() / 2, getHeight() / 2, innerCircleRadiusProgress * (maxCircleSize + 1), maskPaint); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setMaxDotSize(int pxUnits) { | ||||||
|  |         maxDotSize = pxUnits; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void drawOuterDotsFrame(Canvas canvas) { | ||||||
|  |         for (int i = 0; i < DOTS_COUNT; i++) { | ||||||
|  |             int cX = (int) (centerX + currentRadius1 * Math.cos(i * OUTER_DOTS_POSITION_ANGLE * Math.PI / 180)); | ||||||
|  |             int cY = (int) (centerY + currentRadius1 * Math.sin(i * OUTER_DOTS_POSITION_ANGLE * Math.PI / 180)); | ||||||
|  |             canvas.drawCircle(cX, cY, currentDotSize1, dotsPaints[i % dotsPaints.length]); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void drawInnerDotsFrame(Canvas canvas) { | ||||||
|  |         for (int i = 0; i < DOTS_COUNT; i++) { | ||||||
|  |             int cX = (int) (centerX + currentRadius2 * Math.cos((i * OUTER_DOTS_POSITION_ANGLE - 10) * Math.PI / 180)); | ||||||
|  |             int cY = (int) (centerY + currentRadius2 * Math.sin((i * OUTER_DOTS_POSITION_ANGLE - 10) * Math.PI / 180)); | ||||||
|  |             canvas.drawCircle(cX, cY, currentDotSize2, dotsPaints[(i + 1) % dotsPaints.length]); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public float getCurrentProgress() { | ||||||
|  |         return currentProgress; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setCurrentProgress(float currentProgress) { | ||||||
|  |         this.currentProgress = currentProgress; | ||||||
|  | 
 | ||||||
|  |         updateInnerDotsPosition(); | ||||||
|  |         updateOuterDotsPosition(); | ||||||
|  |         updateDotsPaints(); | ||||||
|  |         updateDotsAlpha(); | ||||||
|  | 
 | ||||||
|  |         postInvalidate(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void updateInnerDotsPosition() { | ||||||
|  |         if (currentProgress < 0.3f) { | ||||||
|  |             this.currentRadius2 = (float) Utils.mapValueFromRangeToRange(currentProgress, 0, 0.3f, 0.f, maxInnerDotsRadius); | ||||||
|  |         } else { | ||||||
|  |             this.currentRadius2 = maxInnerDotsRadius; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (currentProgress < 0.2) { | ||||||
|  |             this.currentDotSize2 = maxDotSize; | ||||||
|  |         } else if (currentProgress < 0.5) { | ||||||
|  |             this.currentDotSize2 = (float) Utils.mapValueFromRangeToRange(currentProgress, 0.2f, 0.5f, maxDotSize, 0.3 * maxDotSize); | ||||||
|  |         } else { | ||||||
|  |             this.currentDotSize2 = (float) Utils.mapValueFromRangeToRange(currentProgress, 0.5f, 1f, maxDotSize * 0.3f, 0); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void updateOuterDotsPosition() { | ||||||
|  |         if (currentProgress < 0.3f) { | ||||||
|  |             this.currentRadius1 = (float) Utils.mapValueFromRangeToRange(currentProgress, 0.0f, 0.3f, 0, maxOuterDotsRadius * 0.8f); | ||||||
|  |         } else { | ||||||
|  |             this.currentRadius1 = (float) Utils.mapValueFromRangeToRange(currentProgress, 0.3f, 1f, 0.8f * maxOuterDotsRadius, maxOuterDotsRadius); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (currentProgress < 0.7) { | ||||||
|  |             this.currentDotSize1 = maxDotSize; | ||||||
|  |         } else { | ||||||
|  |             this.currentDotSize1 = (float) Utils.mapValueFromRangeToRange(currentProgress, 0.7f, 1f, maxDotSize, 0); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void updateDotsPaints() { | ||||||
|  |         if (currentProgress < 0.5f) { | ||||||
|  |             float progress = (float) Utils.mapValueFromRangeToRange(currentProgress, 0f, 0.5f, 0, 1f); | ||||||
|  |             dotsPaints[0].setColor((Integer) argbEvaluator.evaluate(progress, primaryColor, primaryColorDark)); | ||||||
|  |             dotsPaints[1].setColor((Integer) argbEvaluator.evaluate(progress, primaryColorDark, secondaryColor)); | ||||||
|  |             dotsPaints[2].setColor((Integer) argbEvaluator.evaluate(progress, secondaryColor, secondaryColorDark)); | ||||||
|  |             dotsPaints[3].setColor((Integer) argbEvaluator.evaluate(progress, secondaryColorDark, primaryColor)); | ||||||
|  |         } else { | ||||||
|  |             float progress = (float) Utils.mapValueFromRangeToRange(currentProgress, 0.5f, 1f, 0, 1f); | ||||||
|  |             dotsPaints[0].setColor((Integer) argbEvaluator.evaluate(progress, primaryColorDark, secondaryColor)); | ||||||
|  |             dotsPaints[1].setColor((Integer) argbEvaluator.evaluate(progress, secondaryColor, secondaryColorDark)); | ||||||
|  |             dotsPaints[2].setColor((Integer) argbEvaluator.evaluate(progress, secondaryColorDark, primaryColor)); | ||||||
|  |             dotsPaints[3].setColor((Integer) argbEvaluator.evaluate(progress, primaryColor, primaryColorDark)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void updateDotsAlpha() { | ||||||
|  |         float progress = (float) Utils.clamp(currentProgress, 0.6f, 1f); | ||||||
|  |         int alpha = (int) Utils.mapValueFromRangeToRange(progress, 0.6f, 1f, 255, 0); | ||||||
|  |         dotsPaints[0].setAlpha(alpha); | ||||||
|  |         dotsPaints[1].setAlpha(alpha); | ||||||
|  |         dotsPaints[2].setAlpha(alpha); | ||||||
|  |         dotsPaints[3].setAlpha(alpha); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setColors(int primaryColor, int secondaryColor) { | ||||||
|  |         this.primaryColor = primaryColor; | ||||||
|  |         this.primaryColorDark = Utils.darkenColor(primaryColor, 1.1f); | ||||||
|  |         this.secondaryColor = secondaryColor; | ||||||
|  |         this.secondaryColorDark = Utils.darkenColor(secondaryColor, 1.1f); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public float getInnerCircleRadiusProgress() { | ||||||
|  |         return innerCircleRadiusProgress; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setInnerCircleRadiusProgress(float innerCircleRadiusProgress) { | ||||||
|  |         this.innerCircleRadiusProgress = innerCircleRadiusProgress; | ||||||
|  |         postInvalidate(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void updateCircleColor() { | ||||||
|  |         float colorProgress = (float) Utils.clamp(outerCircleRadiusProgress, 0.5, 1); | ||||||
|  |         colorProgress = (float) Utils.mapValueFromRangeToRange(colorProgress, 0.5f, 1f, 0f, 1f); | ||||||
|  |         this.circlePaint.setColor((Integer) argbEvaluator.evaluate(colorProgress, primaryColor, secondaryColor)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public float getOuterCircleRadiusProgress() { | ||||||
|  |         return outerCircleRadiusProgress; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setOuterCircleRadiusProgress(float outerCircleRadiusProgress) { | ||||||
|  |         this.outerCircleRadiusProgress = outerCircleRadiusProgress; | ||||||
|  |         updateCircleColor(); | ||||||
|  |         postInvalidate(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,30 @@ | ||||||
|  | package com.varunest.sparkbutton.helpers; | ||||||
|  | 
 | ||||||
|  | import android.content.Context; | ||||||
|  | import android.graphics.Color; | ||||||
|  | import android.util.DisplayMetrics; | ||||||
|  | 
 | ||||||
|  | import androidx.annotation.NonNull; | ||||||
|  | 
 | ||||||
|  | public class Utils { | ||||||
|  |     public static double mapValueFromRangeToRange(double value, double fromLow, double fromHigh, double toLow, double toHigh) { | ||||||
|  |         return toLow + ((value - fromLow) / (fromHigh - fromLow) * (toHigh - toLow)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public static double clamp(double value, double low, double high) { | ||||||
|  |         return Math.min(Math.max(value, low), high); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public static int darkenColor(int color, float multiplier) { | ||||||
|  |         float[] hsv = new float[3]; | ||||||
|  | 
 | ||||||
|  |         Color.colorToHSV(color, hsv); | ||||||
|  |         hsv[2] *= multiplier; // value component | ||||||
|  |         return Color.HSVToColor(hsv); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public static int dpToPx(@NonNull Context context, int dp) { | ||||||
|  |         DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); | ||||||
|  |         return Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT)); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								sparkbutton/src/main/res/values/attrs.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								sparkbutton/src/main/res/values/attrs.xml
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | ||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <resources> | ||||||
|  |     <declare-styleable name="SparkButton"> | ||||||
|  |         <attr name="iconSize" format="dimension|reference" /> | ||||||
|  |         <attr name="activeImage" format="reference" /> | ||||||
|  |         <attr name="inactiveImage" format="reference" /> | ||||||
|  |         <attr name="primaryColor" format="reference" /> | ||||||
|  |         <attr name="secondaryColor" format="reference" /> | ||||||
|  |         <attr name="animationSpeed" format="float" /> | ||||||
|  |     </declare-styleable> | ||||||
|  | </resources> | ||||||
							
								
								
									
										6
									
								
								sparkbutton/src/main/res/values/colors.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								sparkbutton/src/main/res/values/colors.xml
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,6 @@ | ||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <resources> | ||||||
|  |     <color name="spark_primary_color">#FFFFC107</color> | ||||||
|  |     <color name="spark_secondary_color">#FFFF5722</color> | ||||||
|  |     <color name="spark_image_tint">#00000000</color> | ||||||
|  | </resources> | ||||||
							
								
								
									
										3
									
								
								sparkbutton/src/main/res/values/strings.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								sparkbutton/src/main/res/values/strings.xml
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | ||||||
|  | <resources> | ||||||
|  |     <string name="app_name">SparkButton</string> | ||||||
|  | </resources> | ||||||
		Loading…
	
		Reference in a new issue