Skip to content

Commit

Permalink
Feature: added more utils.
Browse files Browse the repository at this point in the history
  • Loading branch information
pokk committed Feb 28, 2019
1 parent 046da46 commit 30da947
Show file tree
Hide file tree
Showing 39 changed files with 393 additions and 274 deletions.
5 changes: 3 additions & 2 deletions kotlinknifer/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,15 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
// 'compile' can also import to the new project with this.
compile "com.google.code.gson:gson:2.8.5"
compile "com.github.bumptech.glide:glide:4.8.0"
compile "com.github.bumptech.glide:glide:4.9.0"
// 'implementation' can't import to the new project.
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutine_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$kotlin_coroutine_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutine_version"
implementation "org.jetbrains.anko:anko-commons:0.10.8"
implementation "androidx.appcompat:appcompat:1.0.2"
implementation "androidx.recyclerview:recyclerview:1.1.0-alpha01"
implementation "androidx.core:core-ktx:1.0.1"
implementation "androidx.recyclerview:recyclerview:1.1.0-alpha02"
implementation "androidx.palette:palette:1.0.0"
compile project(":kotlinshaver")
}
Expand Down
113 changes: 113 additions & 0 deletions kotlinknifer/src/main/java/com/devrapid/kotlinknifer/Animation.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package com.devrapid.kotlinknifer

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ArgbEvaluator
import android.animation.ValueAnimator
import android.os.Build
import android.os.Parcel
import android.os.Parcelable
import android.view.View
import android.view.ViewAnimationUtils
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import kotlin.math.pow

fun View.registerCircularRevealAnimation(
revealSettings: RevealAnimationSetting,
startColor: Int,
endColor: Int
) {
val duration = 1000L
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
override fun onLayoutChange(
v: View,
left: Int,
top: Int,
right: Int,
bottom: Int,
oldLeft: Int,
oldTop: Int,
oldRight: Int,
oldBottom: Int
) {
v.removeOnLayoutChangeListener(this)
val (cx, cy, width, height) = revealSettings

// Simply use the diagonal of the view.
val radius = Math.sqrt((width.toDouble().pow(2) + height.toDouble().pow(2))).toFloat()
ViewAnimationUtils.createCircularReveal(v, cx, cy, 0f, radius).apply {
this.duration = duration
interpolator = FastOutSlowInInterpolator()
}.start()
}
})
startColorAnimation(startColor, endColor, duration)
}
}

fun View.startCircularExitAnimation(
revealSettings: RevealAnimationSetting,
startColor: Int,
endColor: Int,
listener: () -> Unit
) {
val duration = 1000L
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val (cx, cy, width, height) = revealSettings

// Simply use the diagonal of the view.
val radius = Math.sqrt((width.toDouble().pow(2) + height.toDouble().pow(2))).toFloat()
ViewAnimationUtils.createCircularReveal(this, cx, cy, radius, 0f).apply {
this.duration = duration
interpolator = FastOutSlowInInterpolator()
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
gone()
listener()
}
})
}.start()
startColorAnimation(startColor, endColor, duration)
}
else {
listener()
}
}

fun View.startColorAnimation(
startColor: Int,
endColor: Int,
duration: Long
) {
ValueAnimator().apply {
this.duration = duration
setIntValues(startColor, endColor)
setEvaluator(ArgbEvaluator())
addUpdateListener { setBackgroundColor(it.animatedValue as Int) }
}.start()
}

data class RevealAnimationSetting(
var centerX: Int,
var centerY: Int,
var width: Int,
var height: Int
) : Parcelable {
companion object CREATOR : Parcelable.Creator<RevealAnimationSetting> {
override fun createFromParcel(parcel: Parcel) = RevealAnimationSetting(parcel)

override fun newArray(size: Int) = arrayOfNulls<RevealAnimationSetting>(size)
}

constructor(parcel: Parcel) : this(parcel.readInt(), parcel.readInt(), parcel.readInt(), parcel.readInt())

override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(centerX)
parcel.writeInt(centerY)
parcel.writeInt(width)
parcel.writeInt(height)
}

override fun describeContents(): Int = 0
}
64 changes: 52 additions & 12 deletions kotlinknifer/src/main/java/com/devrapid/kotlinknifer/Bitmap.kt
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
package com.devrapid.kotlinknifer

import android.content.Context
import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.BitmapRegionDecoder
import android.graphics.BitmapShader
import android.graphics.Canvas
import android.graphics.ComposeShader
import android.graphics.Paint
import android.graphics.PorterDuff
import android.graphics.Rect
import android.graphics.Shader
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.renderscript.Allocation
import android.renderscript.Element
import android.renderscript.RenderScript
import android.renderscript.ScriptIntrinsicBlur
import android.util.Size
import androidx.annotation.DrawableRes
import androidx.palette.graphics.Palette
import java.io.ByteArrayOutputStream

/**
Expand Down Expand Up @@ -86,19 +96,11 @@ fun Resources.createRegionBitmap(
decoder.decodeRegion(rect, opts)
}

fun Drawable.toBitmap(): Bitmap {
if (this is BitmapDrawable) return bitmap
inline fun Bitmap.toDrawable(context: Context) = BitmapDrawable(context.resources, this)

val width = if (bounds.isEmpty) intrinsicWidth else bounds.width()
val height = if (bounds.isEmpty) intrinsicHeight else bounds.height()
inline fun Bitmap.palette() = Palette.from(this)

return Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also {
Canvas(it).let {
setBounds(0, 0, it.width, it.height)
draw(it)
}
}
}
inline fun Bitmap.palette(maxColorCount: Int) = Palette.from(this).maximumColorCount(maxColorCount).generate()

fun Bitmap.toBytes(format: Bitmap.CompressFormat = Bitmap.CompressFormat.PNG, quality: Int = 100) {
val stream = ByteArrayOutputStream()
Expand All @@ -120,6 +122,44 @@ fun Bitmap?.safeRecycle() {
recycle()
}

fun Bitmap.decorateGradientMask(shaderDst: Shader): Bitmap {
val res = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(res)
// Create the source shader bitmap.
val shaderSrc = BitmapShader(this, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)

val paint = Paint().apply {
shader = ComposeShader(shaderDst, shaderSrc, PorterDuff.Mode.SRC_IN)
}
canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), paint)

return res
}

fun Context.blurBitmap(image: Bitmap, radius: Float = 25f, scale: Float = 0.4f): Bitmap {
val width = Math.round(image.width * scale)
val height = Math.round(image.height * scale)
// Because of the blurring, we don't have to use original bitmap to blur. It's able to reduce cost.
val scaledBitmap = Bitmap.createScaledBitmap(image, width, height, false)
// Create a image for blurring.
val blurBitmap = Bitmap.createBitmap(scaledBitmap)
val rs = RenderScript.create(this)
// RenderScript doesn't use VM to allocate memory, we have to do it by ourselves.
val tmpIn = Allocation.createFromBitmap(rs, scaledBitmap)
// The created Allocation is empty actually, copyTo() is necessary to fill the date.
val tmpOut = Allocation.createFromBitmap(rs, blurBitmap)

ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)).apply {
setRadius(radius)
setInput(tmpIn)
forEach(tmpOut)
}

tmpOut.copyTo(blurBitmap)

return blurBitmap
}

object ImageUtils {
/**
* @param originWidth the width of the origin bitmap
Expand Down
24 changes: 24 additions & 0 deletions kotlinknifer/src/main/java/com/devrapid/kotlinknifer/Bundle.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.devrapid.kotlinknifer

import android.app.Activity
import androidx.fragment.app.Fragment

inline fun <reified T : Any> Activity.extra(key: String, default: T? = null) = lazy {
val value = intent?.extras?.get(key)
if (value is T) value else default
}

inline fun <reified T : Any> Activity.extraNotNull(key: String, default: T? = null) = lazy {
val value = intent?.extras?.get(key)
requireNotNull(if (value is T) value else default) { key }
}

inline fun <reified T : Any> Fragment.extra(key: String, default: T? = null) = lazy {
val value = arguments?.get(key)
if (value is T) value else default
}

inline fun <reified T : Any> Fragment.extraNotNull(key: String, default: T? = null) = lazy {
val value = arguments?.get(key)
requireNotNull(if (value is T) value else default) { key }
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ package com.devrapid.kotlinknifer

import java.util.Stack

/**
* @author jieyi
* @since 2018/01/12
*/
inline fun <E> Stack<E>.safePop() = lastOrNull()?.let { pop() }

inline fun <E> Stack<E>.safePeek() = lastOrNull()?.let { peek() }
43 changes: 15 additions & 28 deletions kotlinknifer/src/main/java/com/devrapid/kotlinknifer/Color.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
@file:Suppress("NOTHING_TO_INLINE", "EXTENSION_SHADOWED_BY_MEMBER")

package com.devrapid.kotlinknifer

import android.content.Context
Expand All @@ -8,12 +6,8 @@ import android.view.View
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment

/**
*
* @author jieyi
* @since 5/30/17
*/
/**
* Add the alpha into the [Color] from resource id.
*
Expand All @@ -23,39 +17,29 @@ import androidx.core.content.ContextCompat
* @return transparent RGB integer resColor
*/
@ColorInt
inline fun Context.getResColorWithAlpha(@ColorRes resColor: Int, ratio: Float): Int =
this.getColorWithAlpha(this.getResColor(resColor), ratio)
inline fun Context.getColorOfAlpha(@ColorRes resColor: Int, ratio: Float) =
ContextCompat.getColor(this, resColor).ofAlpha(ratio)

@ColorInt
inline fun View.getResColorWithAlpha(@ColorRes resColor: Int, ratio: Float): Int =
this.context.getResColorWithAlpha(resColor, ratio)
inline fun Fragment.getColorOfAlpha(@ColorRes resColor: Int, ratio: Float) = getColor(resColor).ofAlpha(ratio)

@ColorInt
inline fun View.getColorOfAlpha(@ColorRes resColor: Int, ratio: Float) = getColor(resColor).ofAlpha(ratio)

/**
* Add the alpha into the Color.
*
* @param color opaque RGB integer color for ex: -11517920
* @param ratio ratio of transparency for ex: 0.5f
*
* @return transparent RGB integer resColor
*/
@ColorInt
inline fun Context.getColorWithAlpha(@ColorInt color: Int, ratio: Float): Int {
val a: Int = Math.round(Color.alpha(color) * ratio)
inline fun Int.ofAlpha(ratio: Float): Int {
val alpha = Math.round(Color.alpha(this) * ratio)

return Color.argb(a, Color.red(color), Color.green(color), Color.blue(color))
return Color.argb(alpha, Color.red(this), Color.green(this), Color.blue(this))
}

@ColorInt
inline fun View.getColorWithAlpha(@ColorInt color: Int, ratio: Float): Int =
this.context.getColorWithAlpha(color, ratio)

@ColorInt
inline fun Context.getColorInt(@ColorInt color: Int) =
Color.rgb(Color.red(color), Color.green(color), Color.blue(color))

@ColorInt
inline fun View.getColorInt(@ColorInt color: Int) = context.getColorInt(color)

/**
* Get the [Color] from resource id.
*
Expand All @@ -64,7 +48,7 @@ inline fun View.getColorInt(@ColorInt color: Int) = context.getColorInt(color)
* @return transparent RGB integer resColor
*/
@ColorInt
inline fun Context.getResColor(@ColorRes resColor: Int): Int = ContextCompat.getColor(this, resColor)
inline fun Fragment.getColor(@ColorRes resColor: Int) = ContextCompat.getColor(requireContext(), resColor)

/**
* Get the [Color] from resource id.
Expand All @@ -74,4 +58,7 @@ inline fun Context.getResColor(@ColorRes resColor: Int): Int = ContextCompat.get
* @return transparent RGB integer resColor
*/
@ColorInt
inline fun View.getResColor(@ColorRes resColor: Int): Int = ContextCompat.getColor(this.context, resColor)
inline fun View.getColor(@ColorRes resColor: Int) = ContextCompat.getColor(context, resColor)

@ColorInt
inline fun generateColorInt(@ColorInt color: Int) = Color.rgb(Color.red(color), Color.green(color), Color.blue(color))
10 changes: 10 additions & 0 deletions kotlinknifer/src/main/java/com/devrapid/kotlinknifer/Context.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.devrapid.kotlinknifer

import android.app.ActivityManager
import android.content.Context
import androidx.appcompat.app.AppCompatActivity

fun Context.closeCurrentTask(activity: AppCompatActivity) =
(getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager).appTasks
.find { it.taskInfo.id == activity.taskId }
?.finishAndRemoveTask()
14 changes: 3 additions & 11 deletions kotlinknifer/src/main/java/com/devrapid/kotlinknifer/Delegate.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,6 @@ import kotlin.properties.Delegates
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty

/**
* Delegate methods.
*
* @author jieyi
* @since 6/12/17
*/
/**
* Wrap the [SoftReference] by kotlin delegate. Don't have to use [get]/[set] method
* for accessing or assigning a variable each of times.
Expand Down Expand Up @@ -125,10 +119,8 @@ class SharedPrefs<T>(var defaultValue: T, val objectType: Class<T>? = null, var
is Long -> prefs.getLong(name, defaultValue as Long) as T
is String -> prefs.getString(name, defaultValue as String) as T
is Set<*> -> prefs.getStringSet(name, defaultValue as Set<String>) as T
// Using json format to deserialize a string to an object.
else -> this.gson.fromJson(prefs.getString(name,
null) ?: throw KotlinNullPointerException("There is no kind of $name was stored in the shared preferences."),
objectType)
// Using json format to deserialize a string to an object.
else -> prefs.getString(name, null)?.let { this.gson.fromJson(it, objectType) } ?: defaultValue
}
}

Expand All @@ -143,7 +135,7 @@ class SharedPrefs<T>(var defaultValue: T, val objectType: Class<T>? = null, var
is Long -> it.putLong(name, value as Long)
is String -> it.putString(name, value as String)
is Set<*> -> it.putStringSet(name, value as Set<String>)
// Using json format to serialize an object to a string.
// Using json format to serialize an object to a string.
else -> it.putString(name, this.gson.toJson(value))
}
this.onChange?.invoke()
Expand Down
Loading

0 comments on commit 30da947

Please sign in to comment.