Skip to content

Commit

Permalink
Generalizes animation framework and adds support for icon animation
Browse files Browse the repository at this point in the history
  • Loading branch information
aritraroy committed Mar 25, 2018
1 parent fbadda3 commit e5b0a13
Show file tree
Hide file tree
Showing 7 changed files with 262 additions and 146 deletions.
21 changes: 14 additions & 7 deletions app/src/main/java/com/andrognito/flashbar/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,29 @@ class MainActivity : AppCompatActivity() {
show.setOnClickListener {
if (flashbar == null) {
flashbar = Flashbar.Builder(activity)
.position(Flashbar.FlashbarPosition.TOP)
.position(Flashbar.FlashbarPosition.BOTTOM)
.backgroundColorRes(R.color.colorPrimary)
.title("Hello!")
.enterAnimation(FlashAnim
.with(activity)
.enterFrom(FlashAnim.Position.TOP)
.bounce()
.duration(350)
.animateBar()
.enterFrom(FlashAnim.Position.BOTTOM)
.overshoot()
.duration(250)
.build())
.exitAnimation(FlashAnim
.with(activity)
.exitFrom(FlashAnim.Position.TOP)
.bounce()
.duration(350)
.animateBar()
.exitFrom(FlashAnim.Position.BOTTOM)
.overshoot()
.duration(250)
.build())
.actionText("Close")
.iconAnimation(FlashAnim.with(activity)
.animateIcon()
.pulse()
.duration(400)
.build())
.modalOverlayColorRes(R.color.modal)
.enableSwipeToDismiss()
.vibrateOn(Flashbar.Vibration.SHOW, Flashbar.Vibration.DISMISS)
Expand Down
145 changes: 15 additions & 130 deletions flashbar/src/main/java/com/andrognito/flashbar/FlashAnim.kt
Original file line number Diff line number Diff line change
@@ -1,148 +1,33 @@
package com.andrognito.flashbar

import android.content.Context
import android.support.annotation.AnimRes
import android.support.annotation.InterpolatorRes
import android.view.animation.Animation
import android.view.animation.AnimationSet
import android.view.animation.AnimationUtils
import android.view.animation.Interpolator
import com.andrognito.flashbar.FlashAnim.Position.BOTTOM
import com.andrognito.flashbar.FlashAnim.Position.TOP

class FlashAnim {

private lateinit var animationSet: AnimationSet

fun getAnimation() = animationSet
internal lateinit var animation: AnimationSet

companion object {
fun with(context: Context): FlashAnimBuilder = FlashAnimBuilder(context)
fun with(context: Context) = FlashAnimBuilderRetriever(context)
}

class FlashAnimBuilder(private val context: Context) {
private val DEFAULT_ANIM_DURATION = context.resources
.getInteger(R.integer.default_animation_duration).toLong()

private val ANIM_ENTER_TOP = AnimationUtils.loadAnimation(context, R.anim.anim_enter_top)
private val ANIM_ENTER_BOTTOM = AnimationUtils.loadAnimation(context, R.anim.anim_enter_bottom)
private val ANIM_EXIT_TOP = AnimationUtils.loadAnimation(context, R.anim.anim_exit_top)
private val ANIM_EXIT_BOTTOM = AnimationUtils.loadAnimation(context, R.anim.anim_exit_bottom)
private val ANIM_ALPHA_ENTER = AnimationUtils.loadAnimation(context, R.anim.anim_alpha_enter)
private val ANIM_ALPHA_EXIT = AnimationUtils.loadAnimation(context, R.anim.anim_alpha_exit)

private val INTERPOLATOR_ACCELERATE = AnimationUtils.loadInterpolator(context, android.R.anim.accelerate_interpolator)
private val INTERPOLATOR_DECELERATE = AnimationUtils.loadInterpolator(context, android.R.anim.decelerate_interpolator)
private val INTERPOLATOR_ACCELERATE_DECELERATE = AnimationUtils.loadInterpolator(context, android.R.anim.accelerate_decelerate_interpolator)
private val INTERPOLATOR_BOUNCE = AnimationUtils.loadInterpolator(context, android.R.anim.bounce_interpolator)
private val INTERPOLATOR_OVERSHOOT = AnimationUtils.loadInterpolator(context, android.R.anim.overshoot_interpolator)

private lateinit var positionAnim: Animation
private lateinit var alphaAnim: Animation
private var enterPosition: Position? = null
private var exitPosition: Position? = null
private var duration: Long? = null
private var alpha: Boolean = false

internal var interpolator: Interpolator? = null

fun enterFrom(position: Position) = apply {
this.enterPosition = position
}

fun exitFrom(position: Position) = apply {
this.exitPosition = position
}

fun customAnimation(animation: Animation) = apply {
this.positionAnim = animation
}

fun customAnimation(@AnimRes id: Int) = apply {
this.positionAnim = AnimationUtils.loadAnimation(context, id)
}

fun alpha(alpha: Boolean) = apply { this.alpha = alpha }

fun accelerate() = apply {
this.interpolator = INTERPOLATOR_ACCELERATE
}

fun decelerate() = apply {
this.interpolator = INTERPOLATOR_DECELERATE
}

fun accelerateDecelerate() = apply {
this.interpolator = INTERPOLATOR_ACCELERATE_DECELERATE
}

fun bounce() = apply {
this.interpolator = INTERPOLATOR_BOUNCE
}

fun overshoot() = apply {
this.interpolator = INTERPOLATOR_OVERSHOOT
}

fun interpolator(interpolator: Interpolator) = apply {
this.interpolator = interpolator
}

fun interpolator(@InterpolatorRes id: Int) = apply {
this.interpolator = AnimationUtils.loadInterpolator(context, id)
}

fun duration(duration: Long) = apply {
this.duration = duration
}

fun build(): FlashAnim {
require((enterPosition != null).xor(exitPosition != null),
{ "You must specify at least one of the animations but not both" })

val flashAnim = FlashAnim()
val animationSet = AnimationSet(false)
animationSet.fillAfter = true

if (!::positionAnim.isInitialized) {
if (enterPosition != null) {
when (enterPosition) {
TOP -> positionAnim = ANIM_ENTER_TOP
BOTTOM -> positionAnim = ANIM_ENTER_BOTTOM
}

if (alpha) alphaAnim = ANIM_ALPHA_ENTER
}

if (exitPosition != null) {
when (exitPosition) {
TOP -> positionAnim = ANIM_EXIT_TOP
BOTTOM -> positionAnim = ANIM_EXIT_BOTTOM
}

if (alpha) alphaAnim = ANIM_ALPHA_EXIT

}
}

animationSet.addAnimation(positionAnim)
if (::alphaAnim.isInitialized) {
animationSet.addAnimation(alphaAnim)
}

animationSet.duration = if (duration != null) duration!! else DEFAULT_ANIM_DURATION

if (interpolator != null) {
positionAnim.interpolator = interpolator
}

flashAnim.animationSet = animationSet
return flashAnim
}
enum class Type {
ENTER,
EXIT,
}

enum class Position {
TOP,
BOTTOM,
}

enum class Alpha {
IN,
OUT,
}
}

class FlashAnimBuilderRetriever(private val context: Context) {
fun animateBar() = FlashAnimBarBuilder(context)
fun animateIcon() = FlashAnimIconBuilder(context)
}
187 changes: 187 additions & 0 deletions flashbar/src/main/java/com/andrognito/flashbar/FlashAnimIconBuilder.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
package com.andrognito.flashbar

import android.content.Context
import android.support.annotation.AnimRes
import android.support.annotation.InterpolatorRes
import android.view.animation.Animation
import android.view.animation.AnimationSet
import android.view.animation.AnimationUtils
import android.view.animation.Interpolator
import com.andrognito.flashbar.FlashAnim.Alpha.IN
import com.andrognito.flashbar.FlashAnim.Alpha.OUT
import com.andrognito.flashbar.FlashAnim.Position.BOTTOM
import com.andrognito.flashbar.FlashAnim.Position.TOP
import com.andrognito.flashbar.FlashAnim.Type.ENTER
import com.andrognito.flashbar.FlashAnim.Type.EXIT

abstract class BaseFlashAnimBuilder(private val context: Context) {
private val DEFAULT_ANIM_DURATION = context.resources
.getInteger(R.integer.default_animation_duration).toLong()

private val INTERPOLATOR_LINEAR = AnimationUtils.loadInterpolator(context,
android.R.anim.linear_interpolator)
private val INTERPOLATOR_ACCELERATE = AnimationUtils.loadInterpolator(context,
android.R.anim.accelerate_interpolator)
private val INTERPOLATOR_DECELERATE = AnimationUtils.loadInterpolator(context,
android.R.anim.decelerate_interpolator)
private val INTERPOLATOR_ACCELERATE_DECELERATE = AnimationUtils.loadInterpolator(context,
android.R.anim.accelerate_decelerate_interpolator)
private val INTERPOLATOR_OVERSHOOT = AnimationUtils.loadInterpolator(context,
android.R.anim.overshoot_interpolator)
private val INTERPOLATOR_ANTICIPATE_OVERSHOOT = AnimationUtils.loadInterpolator(context,
android.R.anim.anticipate_overshoot_interpolator)

protected val ANIM_SLIDE_FROM_TOP = AnimationUtils.loadAnimation(context, R.anim.anim_enter_top)
protected val ANIM_SLIDE_FROM_BOTTOM = AnimationUtils.loadAnimation(context, R.anim.anim_enter_bottom)
protected val ANIM_SLIDE_TO_TOP = AnimationUtils.loadAnimation(context, R.anim.anim_exit_top)
protected val ANIM_SLIDE_TO_BOTTOM = AnimationUtils.loadAnimation(context, R.anim.anim_exit_bottom)
protected val ANIM_ALPHA_IN = AnimationUtils.loadAnimation(context, R.anim.anim_alpha_enter)
protected val ANIM_ALPHA_OUT = AnimationUtils.loadAnimation(context, R.anim.anim_alpha_exit)

protected var duration: Long = DEFAULT_ANIM_DURATION
protected var interpolator: Interpolator = INTERPOLATOR_LINEAR
protected var animation: Animation? = null
protected var alpha: FlashAnim.Alpha? = null

fun accelerate() = apply {
this.interpolator = INTERPOLATOR_ACCELERATE
}

fun decelerate() = apply {
this.interpolator = INTERPOLATOR_DECELERATE
}

fun accelerateDecelerate() = apply {
this.interpolator = INTERPOLATOR_ACCELERATE_DECELERATE
}

fun overshoot() = apply {
this.interpolator = INTERPOLATOR_OVERSHOOT
}

fun anticipateOvershoot() = apply {
this.interpolator = INTERPOLATOR_ANTICIPATE_OVERSHOOT
}

fun interpolator(interpolator: Interpolator) = apply {
this.interpolator = interpolator
}

fun interpolator(@InterpolatorRes id: Int) = apply {
this.interpolator = AnimationUtils.loadInterpolator(context, id)
}

fun animation(animation: Animation) = apply {
this.animation = animation
}

fun animation(@AnimRes id: Int) = apply {
this.animation = AnimationUtils.loadAnimation(context, id)
}

fun alphaIn() = apply { this.alpha = IN }

fun alphaOut() = apply { this.alpha = OUT }

fun duration(millis: Long) = apply {
this.duration = millis
}

abstract fun build(): FlashAnim
}

class FlashAnimIconBuilder(context: Context) : BaseFlashAnimBuilder(context) {

private val ANIM_PULSE = AnimationUtils.loadAnimation(context, R.anim.anim_pulse)

fun pulse() = apply { this.animation = ANIM_PULSE }

override fun build(): FlashAnim {
val flashAnim = FlashAnim()
val animationSet = AnimationSet(false)
animationSet.fillAfter = true
animationSet.addAnimation(animation)
animationSet.duration = duration
animationSet.interpolator = interpolator

if (alpha != null) {
when (alpha) {
IN -> animationSet.addAnimation(ANIM_ALPHA_IN)
OUT -> animationSet.addAnimation(ANIM_ALPHA_OUT)
}
}

flashAnim.animation = animationSet
return flashAnim
}
}

class FlashAnimBarBuilder(context: Context) : BaseFlashAnimBuilder(context) {

private var type: FlashAnim.Type? = null
private var position: FlashAnim.Position? = null
private var alphaEnabled: Boolean = false

fun enterFrom(position: FlashAnim.Position) = apply {
require(this.type == null && this.position == null,
{ "Animation position is already initialized" })

this.type = ENTER
this.position = position
}

fun exitFrom(position: FlashAnim.Position) = apply {
require(this.type == null && this.position == null,
{ "Animation position is already initialized" })

this.type = EXIT
this.position = position
}

fun alpha() = apply { this.alphaEnabled = true }

override fun build(): FlashAnim {
require(type != null && position != null,
{ "You must specify the animation position" })

val flashAnim = FlashAnim()
val animationSet = AnimationSet(false)
animationSet.fillAfter = true
animationSet.duration = duration
animationSet.interpolator = interpolator

// Only if custom animation is not applied
if (animation == null) {
when (type) {
ENTER -> {
when (position) {
TOP -> animation = ANIM_SLIDE_FROM_TOP
BOTTOM -> animation = ANIM_SLIDE_FROM_BOTTOM
}
}
EXIT -> {
when (position) {
TOP -> animation = ANIM_SLIDE_TO_TOP
BOTTOM -> animation = ANIM_SLIDE_TO_BOTTOM
}
}
}
}
animationSet.addAnimation(animation)

if (alphaEnabled) {
when (type) {
ENTER -> animationSet.addAnimation(ANIM_ALPHA_IN)
EXIT -> animationSet.addAnimation(ANIM_ALPHA_OUT)
}
} else if (alpha != null) {
when (alpha) {
IN -> animationSet.addAnimation(ANIM_ALPHA_IN)
OUT -> animationSet.addAnimation(ANIM_ALPHA_OUT)
}
}

flashAnim.animation = animationSet
return flashAnim
}
}
Loading

0 comments on commit e5b0a13

Please sign in to comment.