Commit 39ba6799 authored by Suphon Thanakornpakapong's avatar Suphon Thanakornpakapong Committed by Romain Hunault
Browse files

Auto adapt text color based on backgroud

parent aec23506
......@@ -67,3 +67,4 @@ lint/tmp/
#ktlint
ktlint
keystore/platform.jks
......@@ -11,6 +11,17 @@ def versionMajor = 1
def versionMinor = 3
def versionPatch = 2
Properties localProps = new Properties()
File localPropsFile = project.rootProject.file('local.properties')
if (localPropsFile.exists()) {
localProps.load(localPropsFile.newDataInputStream())
}
def appendDebugSuffix = (localProps.getProperty('appendDebugSuffix') ?: "true").toBoolean()
def keyStorePath = localProps.getProperty('keyStorePath') ?: "/keystore/debug.keystore"
def keyStorePassword = localProps.getProperty('keyStorePassword') ?: "android"
def signingKeyAlias = localProps.getProperty('keyAlias') ?: "androiddebugkey"
def signingKeyPassword = localProps.getProperty('keyPassword') ?: "android"
android {
compileSdkVersion Versions.compile_sdk
defaultConfig {
......@@ -28,9 +39,12 @@ android {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.debug
}
debug {
applicationIdSuffix '.debug'
if (appendDebugSuffix) {
applicationIdSuffix '.debug'
}
signingConfig signingConfigs.debug
}
......@@ -46,10 +60,10 @@ android {
signingConfigs {
debug {
storeFile file(getRootDir().path + "/keystore/debug.keystore")
storePassword 'android'
keyAlias 'androiddebugkey'
keyPassword 'android'
storeFile file(getRootDir().path + keyStorePath)
storePassword keyStorePassword
keyAlias signingKeyAlias
keyPassword signingKeyPassword
}
}
......
......@@ -38,6 +38,9 @@ public class Utilities {
/**
* Use hard coded values to compile with android source.
*/
public static final boolean ATLEAST_R =
Build.VERSION.SDK_INT >= 30;
public static final boolean ATLEAST_OREO =
Build.VERSION.SDK_INT >= 26;
......
package foundation.e.blisslauncher.core.blur
import android.graphics.Bitmap
import android.graphics.BitmapShader
import android.graphics.Canvas
import android.graphics.ColorFilter
import android.graphics.Paint
import android.graphics.PixelFormat
import android.graphics.RectF
import android.graphics.Shader
import android.graphics.drawable.Drawable
class BlurDrawable internal constructor(private val blurWallpaperProvider: BlurWallpaperProvider) :
Drawable(), BlurWallpaperProvider.Listener {
private var blurAlpha = 255
private val blurPaint = Paint(Paint.FILTER_BITMAP_FLAG or Paint.ANTI_ALIAS_FLAG)
private var blurBitmap: Bitmap? = null
set(value) {
if (field != value) {
field = value
blurPaint.shader =
value?.let { BitmapShader(it, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP) }
}
}
private val blurBounds = RectF()
private var offsetX = 0f
private var offsetY = 0f
override fun draw(canvas: Canvas) {
val width = blurBounds.right.toInt() - blurBounds.left.toInt()
val height = blurBounds.bottom.toInt() - blurBounds.top.toInt()
if (width <= 0 || height <= 0) return
if (blurAlpha == 0) return
blurBitmap = blurWallpaperProvider.wallpapers?.second
if (blurBitmap == null) {
blurBitmap = blurWallpaperProvider.placeholder
}
val left = blurBounds.left + offsetX
val top = blurBounds.top + offsetY
val right = blurBounds.right + offsetX
val bottom = blurBounds.bottom + offsetY
canvas.translate(-left, -top)
canvas.drawRect(left, top, right, bottom, blurPaint)
canvas.translate(left, top)
}
override fun setAlpha(alpha: Int) {
blurAlpha = alpha
blurPaint.alpha = alpha
}
override fun getAlpha(): Int {
return blurAlpha
}
override fun setBounds(left: Int, top: Int, right: Int, bottom: Int) =
setBlurBounds(left.toFloat(), top.toFloat(), right.toFloat(), bottom.toFloat())
fun setBlurBounds(left: Float, top: Float, right: Float, bottom: Float) {
if (blurBounds.left != left ||
blurBounds.top != top ||
blurBounds.right != right ||
blurBounds.bottom != bottom
) {
blurBounds.set(left, top, right, bottom)
}
}
fun setOffsets(offsetX: Float, offsetY: Float) {
this.offsetX = offsetX
this.offsetY = offsetY
}
override fun getOpacity(): Int = PixelFormat.TRANSLUCENT
override fun onWallpaperChanged() {
invalidateSelf()
}
override fun setColorFilter(colorFilter: ColorFilter?) {
}
fun startListening() = blurWallpaperProvider.addListener(this)
fun stopListening() = blurWallpaperProvider.removeListener(this)
}
package foundation.e.blisslauncher.core.blur
import android.graphics.BlendMode
import android.graphics.Canvas
import android.graphics.Outline
import android.graphics.Paint
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import android.view.View
import android.view.ViewOutlineProvider
import android.view.ViewTreeObserver
import android.widget.ScrollView
import foundation.e.blisslauncher.R
import foundation.e.blisslauncher.core.customviews.HorizontalPager
import foundation.e.blisslauncher.core.utils.OffsetParent
class BlurViewDelegate(
private val view: View,
attrs: AttributeSet? = null
) : View.OnAttachStateChangeListener, BlurWallpaperProvider.Listener {
private val context = view.context
private val blurWallpaperProvider by lazy { BlurWallpaperProvider.getInstance(context) }
private var fullBlurDrawable: BlurDrawable? = null
private var blurAlpha = 255
private val blurDrawableCallback by lazy {
object : Drawable.Callback {
override fun unscheduleDrawable(who: Drawable, what: Runnable) {
}
override fun invalidateDrawable(who: Drawable) {
view.post(view::invalidate)
}
override fun scheduleDrawable(who: Drawable, what: Runnable, `when`: Long) {
}
}
}
private var offsetParents = listOf<OffsetParent>()
set(value) {
field.forEach { it.removeOnOffsetChangeListener(onOffsetChangeListener) }
field = value
field.forEach { it.addOnOffsetChangeListener(onOffsetChangeListener) }
}
private val scrollViews = mutableListOf<View>()
private var isScrolling = false
private var previousScrollX = 0
private var previousScrollY = 0
private var parentOffsetX = 0f
private var parentOffsetY = 0f
private val onGlobalLayoutListener = ViewTreeObserver.OnGlobalLayoutListener { updateBounds() }
private val onScrollChangedListener = ViewTreeObserver.OnScrollChangedListener {
isScrolling = true
view.invalidate()
}
private val onOffsetChangeListener = object : OffsetParent.OnOffsetChangeListener {
override fun onOffsetChange() {
computeParentOffset()
}
}
var blurCornerRadius = 0f
val outlineProvider = object : ViewOutlineProvider() {
override fun getOutline(view: View, outline: Outline) {
outline.setRoundRect(0, 0, view.width, view.height, blurCornerRadius)
}
}
var overlayColor: Int = 0
set(value) {
field = value
overlayPaint.color = value
}
private val overlayPaint = Paint(Paint.FILTER_BITMAP_FLAG or Paint.ANTI_ALIAS_FLAG).apply {
blendMode = BlendMode.OVERLAY
}
init {
createFullBlurDrawable()
view.addOnAttachStateChangeListener(this)
if (attrs != null) {
val a = context.obtainStyledAttributes(attrs, R.styleable.BlurLayout)
blurCornerRadius = a.getDimension(
R.styleable.BlurLayout_blurCornerRadius,
0f
)
overlayColor = a.getColor(
R.styleable.BlurLayout_blurOverlayColor,
0
)
a.recycle()
}
}
override fun onViewAttachedToWindow(v: View) {
BlurWallpaperProvider.getInstance(context).addListener(this)
fullBlurDrawable?.startListening()
view.viewTreeObserver.addOnGlobalLayoutListener(onGlobalLayoutListener)
view.viewTreeObserver.addOnScrollChangedListener(onScrollChangedListener)
onGlobalLayoutListener.onGlobalLayout()
}
override fun onViewDetachedFromWindow(v: View) {
BlurWallpaperProvider.getInstance(context).removeListener(this)
fullBlurDrawable?.stopListening()
view.viewTreeObserver.removeOnGlobalLayoutListener(onGlobalLayoutListener)
view.viewTreeObserver.removeOnScrollChangedListener(onScrollChangedListener)
scrollViews.clear()
offsetParents = listOf()
}
fun draw(canvas: Canvas) {
if (isScrolling) {
computeScrollOffset()
updateOffsets()
}
fullBlurDrawable?.apply {
alpha = blurAlpha
this.draw(canvas)
}
if (overlayColor != 0) {
canvas.drawRect(
view.left.toFloat(), view.top.toFloat(),
view.right.toFloat(), view.bottom.toFloat(),
overlayPaint
)
}
}
private fun createFullBlurDrawable() {
fullBlurDrawable?.let { if (view.isAttachedToWindow) it.stopListening() }
fullBlurDrawable = blurWallpaperProvider.createBlurDrawable().apply {
callback = blurDrawableCallback
setBounds(view.left, view.top, view.right, view.bottom)
if (view.isAttachedToWindow) startListening()
}
}
override fun onEnabledChanged() {
createFullBlurDrawable()
}
private fun updateBounds() {
scrollViews.clear()
val offsetParents = mutableListOf<OffsetParent>()
var left = 0
var top = 0
var current: View? = view
while (current != null) {
left += current.left
top += current.top
if (current is ScrollView || current is HorizontalPager) {
scrollViews.add(current)
} else if (current is OffsetParent) {
offsetParents.add(current)
}
current = current.parent as? View
}
val right = left + view.width
val bottom = top + view.height
fullBlurDrawable?.setBlurBounds(left.toFloat(), top.toFloat(), right.toFloat(), bottom.toFloat())
view.invalidate()
this.offsetParents = offsetParents
computeScrollOffset()
computeParentOffset()
}
private fun computeScrollOffset() {
var scrollX = 0
var scrollY = 0
scrollViews.forEach {
scrollX -= it.scrollX
scrollY -= it.scrollY
}
if (previousScrollX == scrollX && previousScrollY == scrollY) {
isScrolling = false
return
}
previousScrollX = scrollX
previousScrollY = scrollY
}
private fun computeParentOffset() {
var offsetX = 0f
var offsetY = 0f
offsetParents.forEach {
offsetX += it.offsetX
offsetY += it.offsetY
}
this.parentOffsetX = offsetX
this.parentOffsetY = offsetY
updateOffsets()
view.invalidate()
}
private fun updateOffsets() {
fullBlurDrawable?.setOffsets(previousScrollX.toFloat() + parentOffsetX, previousScrollY.toFloat() + parentOffsetY)
}
}
......@@ -3,31 +3,43 @@ package foundation.e.blisslauncher.core.blur
import android.content.Context
import android.graphics.Bitmap
import com.hoko.blur.HokoBlur
import com.hoko.blur.task.AsyncBlurTask
class BlurWallpaperFilter(private val context: Context) : WallpaperFilter {
typealias BitmapPair = Pair<Bitmap, Bitmap>
private var blurRadius = 8
class BlurWallpaperFilter(private val context: Context) : WallpaperFilter<BitmapPair> {
override fun apply(wallpaper: Bitmap): WallpaperFilter.ApplyTask {
return WallpaperFilter.ApplyTask.create { emitter ->
HokoBlur.with(context)
.scheme(HokoBlur.SCHEME_NATIVE)
.mode(HokoBlur.MODE_STACK)
.radius(blurRadius)
.sampleFactor(8f)
.forceCopy(false)
.needUpscale(true)
.processor()
.asyncBlur(wallpaper, object : AsyncBlurTask.Callback {
override fun onBlurSuccess(bitmap: Bitmap) {
emitter.onSuccess(bitmap)
}
private var blurRadiusSmall = 8
private var blurRadiusLarge = 25
override fun onBlurFailed(error: Throwable?) {
emitter.onError(error!!)
}
})
override fun apply(wallpaper: Bitmap): WallpaperFilter.ApplyTask<BitmapPair> {
return WallpaperFilter.ApplyTask.create { emitter ->
var wallpaperSmallBlur: Bitmap? = null
var wallpaperLargeBlur: Bitmap? = null
try {
wallpaperSmallBlur = HokoBlur.with(context)
.scheme(HokoBlur.SCHEME_NATIVE)
.mode(HokoBlur.MODE_STACK)
.radius(blurRadiusSmall)
.sampleFactor(8f)
.forceCopy(false)
.needUpscale(true)
.processor()
.blur(wallpaper)
wallpaperLargeBlur = HokoBlur.with(context)
.scheme(HokoBlur.SCHEME_NATIVE)
.mode(HokoBlur.MODE_STACK)
.radius(blurRadiusLarge)
.sampleFactor(16f)
.forceCopy(false)
.needUpscale(true)
.processor()
.blur(wallpaper)
emitter.onSuccess(Pair(wallpaperSmallBlur, wallpaperLargeBlur))
} catch (t: Throwable) {
wallpaperSmallBlur?.recycle()
wallpaperLargeBlur?.recycle()
emitter.onError(t)
}
}
}
}
\ No newline at end of file
}
......@@ -29,10 +29,11 @@ class BlurWallpaperProvider(val context: Context) {
private val listeners = ArrayList<Listener>()
private val displayMetrics = DisplayMetrics()
var wallpaper: Bitmap? = null
var wallpapers: Pair<Bitmap, Bitmap>? = null
private set(value) {
if (field != value) {
field?.recycle()
field?.first?.recycle()
field?.second?.recycle()
field = value
}
}
......@@ -49,7 +50,7 @@ class BlurWallpaperProvider(val context: Context) {
private val mUpdateRunnable = Runnable { updateWallpaper() }
private val wallpaperFilter = BlurWallpaperFilter(context)
private var applyTask: WallpaperFilter.ApplyTask? = null
private var applyTask: WallpaperFilter.ApplyTask<Pair<Bitmap, Bitmap>>? = null
private var updatePending = false
......@@ -99,7 +100,7 @@ class BlurWallpaperProvider(val context: Context) {
}
if (!isEnabled) {
wallpaper = null
wallpapers = null
return
}
......@@ -117,7 +118,7 @@ class BlurWallpaperProvider(val context: Context) {
wallpaper = applyVibrancy(wallpaper)
applyTask = wallpaperFilter.apply(wallpaper).setCallback { result, error ->
if (error == null) {
this@BlurWallpaperProvider.wallpaper = result
this@BlurWallpaperProvider.wallpapers = result
runOnMainThread(::notifyWallpaperChanged)
wallpaper.recycle()
} else {
......@@ -213,6 +214,10 @@ class BlurWallpaperProvider(val context: Context) {
return ShaderBlurDrawable(this)
}
fun createBlurDrawable(): BlurDrawable {
return BlurDrawable(this)
}
interface Listener {
fun onWallpaperChanged() {}
fun onEnabledChanged() {}
......
......@@ -44,7 +44,7 @@ class ShaderBlurDrawable internal constructor(private val blurWallpaperProvider:
fun draw(canvas: Canvas, noRadius: Boolean = false) {
if (blurAlpha == 0) return
blurBitmap = blurWallpaperProvider.wallpaper
blurBitmap = blurWallpaperProvider.wallpapers?.first
if (blurBitmap == null) {
blurBitmap = blurWallpaperProvider.placeholder
......
......@@ -2,20 +2,20 @@ package foundation.e.blisslauncher.core.blur
import android.graphics.Bitmap
interface WallpaperFilter {
interface WallpaperFilter<T> {
fun apply(wallpaper: Bitmap): ApplyTask
fun apply(wallpaper: Bitmap): ApplyTask<T>
class ApplyTask {
class ApplyTask<T> {
val emitter = Emitter()
private var result: Bitmap? = null
private var result: T? = null
private var error: Throwable? = null
private var callback: ((Bitmap?, Throwable?) -> Unit)? = null
private var callback: ((T?, Throwable?) -> Unit)? = null
fun setCallback(callback: (Bitmap?, Throwable?) -> Unit): ApplyTask {
fun setCallback(callback: (T?, Throwable?) -> Unit): ApplyTask<T> {
result?.let {
callback(it, null)
return this
......@@ -30,7 +30,7 @@ interface WallpaperFilter {
inner class Emitter {
fun onSuccess(result: Bitmap) {
fun onSuccess(result: T) {
callback?.let {
it(result, null)
return
......@@ -49,8 +49,8 @@ interface WallpaperFilter {
companion object {
inline fun create(source: (Emitter) -> Unit): ApplyTask {
return ApplyTask().also { source(it.emitter) }
inline fun <T> create(source: (ApplyTask<T>.Emitter) -> Unit): ApplyTask<T> {
return ApplyTask<T>().also { source(it.emitter) }
}
}
}
......
......@@ -156,6 +156,7 @@ public class BlissFrameLayout extends FrameLayout {
final SquareFrameLayout icon = findViewById(R.id.app_icon);
final SquareImageView squareImageView = findViewById(
R.id.icon_image_view);
icon.enableBlur();
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) icon.getLayoutParams();
layoutParams.leftMargin = mDeviceProfile.iconDrawablePaddingPx / 2;
layoutParams.rightMargin = mDeviceProfile.iconDrawablePaddingPx / 2;
......
......@@ -2,10 +2,10 @@ package foundation.e.blisslauncher.core.customviews
import android.content.Context
import android.graphics.Canvas
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import android.view.View
import android.view.WindowInsets
import foundation.e.blisslauncher.core.blur.BlurWallpaperProvider
import foundation.e.blisslauncher.core.blur.ShaderBlurDrawable
import foundation.e.blisslauncher.core.runOnMainThread
......@@ -55,6 +55,7 @@ class BlurBackgroundView(context: Context, attrs: AttributeSet?) : View(context,
alpha = blurAlpha
this.draw(canvas)
}
canvas.drawARGB((blurAlpha * 0.01).toInt(), 0, 0, 0)
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
......@@ -76,5 +77,5 @@ class BlurBackgroundView(context: Context, attrs: AttributeSet?) : View(context,
createFullBlurDrawable()
}
override fun setInsets(insets: WindowInsets) {}
}
\ No newline at end of file
override fun setInsets(insets: Rect) {}
}
package foundation.e.blisslauncher.core.customviews
import android.content.Context
import android.graphics.Can