Initial commit
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
package de.mm20.launcher2.graphics
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.*
|
||||
import android.graphics.drawable.AdaptiveIconDrawable
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.LayerDrawable
|
||||
import android.os.Build
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import de.mm20.launcher2.ktx.dp
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class BadgeDrawable(context: Context, drawable: Drawable) : Drawable() {
|
||||
|
||||
private val drawable: BitmapDrawable
|
||||
|
||||
init {
|
||||
val size = (28.8 * context.dp).roundToInt()
|
||||
val drw: Drawable = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && drawable is AdaptiveIconDrawable) {
|
||||
LayerDrawable(arrayOf(
|
||||
drawable.background,
|
||||
drawable.foreground
|
||||
)).apply {
|
||||
val inset = (-size * 0.25).roundToInt()
|
||||
setLayerInset(1, inset, inset, inset, inset)
|
||||
setLayerInset(0, inset, inset, inset, inset)
|
||||
}
|
||||
} else {
|
||||
drawable
|
||||
}
|
||||
val bitmap = drw.toBitmap(size, size).run {
|
||||
if(isMutable) this
|
||||
else this.copy(config, true)
|
||||
}
|
||||
this.drawable = BitmapDrawable(context.resources, bitmap)
|
||||
}
|
||||
|
||||
override fun setAlpha(alpha: Int) {
|
||||
}
|
||||
|
||||
override fun getOpacity(): Int {
|
||||
return PixelFormat.TRANSLUCENT
|
||||
}
|
||||
|
||||
override fun setColorFilter(colorFilter: ColorFilter?) {
|
||||
}
|
||||
|
||||
override fun draw(canvas: Canvas) {
|
||||
canvas.clipPath(Path().apply { addOval(
|
||||
bounds.left.toFloat(),
|
||||
bounds.top.toFloat(),
|
||||
bounds.right.toFloat(),
|
||||
bounds.bottom.toFloat(), Path.Direction.CW) })
|
||||
drawable.setBounds(bounds.left, bounds.top, bounds.right, bounds.bottom)
|
||||
drawable.draw(canvas)
|
||||
}
|
||||
}
|
||||
17
base/src/main/java/de/mm20/launcher2/helper/NetworkUtils.kt
Normal file
17
base/src/main/java/de/mm20/launcher2/helper/NetworkUtils.kt
Normal file
@@ -0,0 +1,17 @@
|
||||
package de.mm20.launcher2.helper
|
||||
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
|
||||
object NetworkUtils {
|
||||
|
||||
fun isOffline(context: Context, allowMobile: Boolean = true): Boolean {
|
||||
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
val info = connectivityManager.activeNetworkInfo ?: return true
|
||||
return when {
|
||||
info.type == ConnectivityManager.TYPE_WIFI -> false
|
||||
info.type == ConnectivityManager.TYPE_MOBILE && allowMobile -> false
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
}
|
||||
84
base/src/main/java/de/mm20/launcher2/icons/LauncherIcon.kt
Normal file
84
base/src/main/java/de/mm20/launcher2/icons/LauncherIcon.kt
Normal file
@@ -0,0 +1,84 @@
|
||||
package de.mm20.launcher2.icons
|
||||
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import androidx.palette.graphics.Palette
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
open class LauncherIcon(
|
||||
foreground: Drawable,
|
||||
background: Drawable? = null,
|
||||
foregroundScale: Float = 1f,
|
||||
backgroundScale: Float = 1f,
|
||||
var autoGenerateBackgroundMode: Int = BACKGROUND_WHITE
|
||||
) {
|
||||
|
||||
var foreground = foreground
|
||||
set(value) {
|
||||
field = value
|
||||
updateBackgroundColor()
|
||||
notifyCallbacks()
|
||||
}
|
||||
|
||||
private fun updateBackgroundColor() {
|
||||
if (background == null) {
|
||||
when (autoGenerateBackgroundMode) {
|
||||
BACKGROUND_COLOR -> {
|
||||
val palette = Palette
|
||||
.from(foreground.toBitmap())
|
||||
.generate()
|
||||
this.background = ColorDrawable(palette.getDominantColor(Color.WHITE))
|
||||
badgeColor = palette.getLightVibrantColor(0xFFF0F0F0.toInt())
|
||||
}
|
||||
BACKGROUND_WHITE -> this.background = ColorDrawable(Color.WHITE)
|
||||
else -> this.foregroundScale = 1f
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var background = background
|
||||
set(value) {
|
||||
field = value
|
||||
notifyCallbacks()
|
||||
}
|
||||
|
||||
var foregroundScale = foregroundScale
|
||||
set(value) {
|
||||
field = value
|
||||
notifyCallbacks()
|
||||
}
|
||||
|
||||
var backgroundScale = backgroundScale
|
||||
set(value) {
|
||||
field = value
|
||||
notifyCallbacks()
|
||||
}
|
||||
|
||||
private val callbacks = mutableListOf<WeakReference<(LauncherIcon) -> Unit>>()
|
||||
|
||||
fun registerCallback(callback: (LauncherIcon) -> Unit) {
|
||||
callbacks.add(WeakReference(callback))
|
||||
}
|
||||
|
||||
protected fun notifyCallbacks() {
|
||||
val iterator = callbacks.iterator()
|
||||
while(iterator.hasNext()) {
|
||||
val callback = iterator.next()
|
||||
callback.get()?.invoke(this) ?: iterator.remove()
|
||||
}
|
||||
}
|
||||
|
||||
var badgeColor: Int = 0xFFF0F0F0.toInt()
|
||||
|
||||
init {
|
||||
updateBackgroundColor()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val BACKGROUND_NONE = 0
|
||||
const val BACKGROUND_COLOR = 1
|
||||
const val BACKGROUND_WHITE = 2
|
||||
}
|
||||
}
|
||||
135
base/src/main/java/de/mm20/launcher2/view/ElevationImageView.kt
Normal file
135
base/src/main/java/de/mm20/launcher2/view/ElevationImageView.kt
Normal file
@@ -0,0 +1,135 @@
|
||||
package de.mm20.launcher2.view
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.*
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.Icon
|
||||
import android.renderscript.Allocation
|
||||
import android.renderscript.Element
|
||||
import android.renderscript.RenderScript
|
||||
import android.renderscript.ScriptIntrinsicBlur
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import com.airbnb.lottie.LottieDrawable
|
||||
import com.airbnb.lottie.LottieProperty
|
||||
import com.airbnb.lottie.model.KeyPath
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
class ElevationImageView : androidx.appcompat.widget.AppCompatImageView {
|
||||
|
||||
constructor(context: Context) : super(context)
|
||||
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleRes: Int) : super(context, attrs, defStyleRes)
|
||||
|
||||
|
||||
private val secondCanvas = Canvas()
|
||||
private var shadowBitmap: Bitmap? = null
|
||||
private var inAllocation: Allocation? = null
|
||||
private lateinit var renderScript : RenderScript
|
||||
private lateinit var blur : ScriptIntrinsicBlur
|
||||
|
||||
val shadowPaint = Paint().also {
|
||||
it.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_OVER)
|
||||
it.colorFilter = PorterDuffColorFilter(Color.BLACK, PorterDuff.Mode.SRC_ATOP)
|
||||
it.alpha = 66
|
||||
setLayerType(View.LAYER_TYPE_SOFTWARE, null)
|
||||
}
|
||||
|
||||
val clearPaint = Paint().also {
|
||||
it.color = Color.TRANSPARENT
|
||||
it.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
blur.destroy()
|
||||
renderScript.destroy()
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
renderScript = RenderScript.create(context)
|
||||
blur = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript))
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
secondCanvas.drawRect(0f, 0f, secondCanvas.width.toFloat(), secondCanvas.height.toFloat(), clearPaint)
|
||||
|
||||
if (drawable == null) {
|
||||
return // couldn't resolve the URI
|
||||
}
|
||||
|
||||
val drawableWidth = drawable.intrinsicWidth
|
||||
val drawableHeight = drawable.intrinsicHeight
|
||||
|
||||
if (drawableWidth == 0 || drawableHeight == 0) {
|
||||
return // nothing to draw (empty bounds)
|
||||
}
|
||||
|
||||
if (shadowBitmap?.width != width || shadowBitmap?.height != height) {
|
||||
shadowBitmap?.recycle()
|
||||
inAllocation?.destroy()
|
||||
if (z > 0f && width > 0 && height > 0) {
|
||||
shadowBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also {
|
||||
secondCanvas.setBitmap(it)
|
||||
}
|
||||
inAllocation = shadowBitmap?.let { Allocation.createFromBitmap(renderScript, it) }
|
||||
}
|
||||
}
|
||||
|
||||
val saveCount = canvas.saveCount
|
||||
val saveCount2 = secondCanvas.saveCount
|
||||
canvas.save()
|
||||
secondCanvas.save()
|
||||
|
||||
|
||||
canvas.translate(paddingLeft.toFloat(), paddingTop.toFloat())
|
||||
secondCanvas.translate(paddingLeft.toFloat(), paddingTop.toFloat() + getShadowTranslation())
|
||||
|
||||
if (!imageMatrix.isIdentity) {
|
||||
canvas.concat(imageMatrix)
|
||||
secondCanvas.concat(imageMatrix)
|
||||
}
|
||||
|
||||
|
||||
drawable.draw(canvas)
|
||||
drawable.draw(secondCanvas)
|
||||
shadowBitmap = shadowBitmap?.let { blurBitmap(it) }
|
||||
canvas.restoreToCount(saveCount)
|
||||
secondCanvas.restoreToCount(saveCount2)
|
||||
shadowBitmap?.let { canvas.drawBitmap(it, 0f, 0f, shadowPaint) }
|
||||
}
|
||||
|
||||
private fun blurBitmap(bitmap: Bitmap): Bitmap {
|
||||
val alloc = inAllocation ?: return bitmap
|
||||
if (z == 0f) return bitmap
|
||||
alloc.copyFrom(bitmap)
|
||||
blur.setRadius(max(0f, min(getShadowBlurRadius(), 25f)))
|
||||
blur.setInput(alloc)
|
||||
blur.forEach(alloc)
|
||||
alloc.copyTo(bitmap)
|
||||
return bitmap
|
||||
}
|
||||
|
||||
override fun setColorFilter(cf: ColorFilter?) {
|
||||
super.setColorFilter(cf)
|
||||
val drawable = drawable
|
||||
if (drawable is LottieDrawable) {
|
||||
drawable.addValueCallback(KeyPath("**"), LottieProperty.COLOR_FILTER) {
|
||||
cf
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun getShadowTranslation(): Float {
|
||||
return z * 0.5f
|
||||
}
|
||||
|
||||
private fun getShadowBlurRadius(): Float {
|
||||
return z * 0.5f
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user