Skip to content

Commit

Permalink
Add AOD burn in protection for lockscreen items on Android 15
Browse files Browse the repository at this point in the history
Currently it is set to trigger every 45 minutes

Signed-off-by: DrDisagree <29881338+Mahmud0808@users.noreply.github.com>
  • Loading branch information
Mahmud0808 committed Feb 1, 2025
1 parent a02178f commit 3071f0d
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package com.drdisagree.iconify.xposed.modules.extras.views

import android.view.View
import android.view.animation.TranslateAnimation
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

class AodBurnInProtection(private val view: View) {

private var isMovementEnabled: Boolean = false
private var originalX: Float = 0f
private var originalY: Float = 0f
private var movementJob: Job? = null

init {
originalX = view.x
originalY = view.y
}

fun setMovementEnabled(enabled: Boolean) {
if (enabled != isMovementEnabled) {
isMovementEnabled = enabled

if (isMovementEnabled) {
startMovement()
} else {
stopMovement()
}
}
}

private fun startMovement() {
if (movementJob?.isActive == true) return

originalX = view.x
originalY = view.y

movementJob = CoroutineScope(Dispatchers.Main).launch {
while (isMovementEnabled) {
moveViewSlightly()
delay(45 * 60 * 1000L) // Delay for 45 minutes
}
}
}

private fun stopMovement() {
movementJob?.cancel()
resetViewPosition()
}

private fun moveViewSlightly() {
val offsetX = (6..10).random().toFloat() * if (Math.random() > 0.5) 1 else -1
val offsetY = (6..10).random().toFloat() * if (Math.random() > 0.5) 1 else -1

val animation = TranslateAnimation(view.x, offsetX, view.y, offsetY)
animation.duration = 1000 // 1 second duration for the movement
animation.fillAfter = true // Keep the final position after the animation finishes

view.startAnimation(animation)
}

private fun resetViewPosition() {
view.animate().x(originalX).y(originalY).setDuration(300).start()
}

companion object {
private val activeMovements = mutableMapOf<View, AodBurnInProtection>()

fun registerForView(view: View): AodBurnInProtection {
return activeMovements.getOrPut(view) {
AodBurnInProtection(view).also {
it.startMovement()
}
}
}

fun unregisterForView(view: View) {
activeMovements[view]?.apply {
stopMovement()
}
activeMovements.remove(view)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ import com.drdisagree.iconify.xposed.modules.extras.utils.toolkit.getFieldSilent
import com.drdisagree.iconify.xposed.modules.extras.utils.toolkit.hookConstructor
import com.drdisagree.iconify.xposed.modules.extras.utils.toolkit.hookMethod
import com.drdisagree.iconify.xposed.modules.extras.utils.toolkit.log
import com.drdisagree.iconify.xposed.modules.extras.views.AodBurnInProtection
import com.drdisagree.iconify.xposed.modules.extras.views.ArcProgressWidget.generateBitmap
import com.drdisagree.iconify.xposed.modules.lockscreen.Lockscreen.Companion.isComposeLockscreen
import com.drdisagree.iconify.xposed.utils.XPrefs.Xprefs
Expand Down Expand Up @@ -134,6 +135,7 @@ class LockscreenClockA15(context: Context) : ModPack(context) {
"${Environment.getExternalStorageDirectory()}/.iconify_files/lsclock_font.ttf"
private val job = Job()
private val coroutineScope = CoroutineScope(Dispatchers.Main + job)
private var aodBurnInProtection: AodBurnInProtection? = null

private val mBatteryReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Expand Down Expand Up @@ -264,6 +266,9 @@ class LockscreenClockA15(context: Context) : ModPack(context) {
mLockscreenRootView = rootView

mLsItemsContainer = rootView.getLsItemsContainer()
aodBurnInProtection =
AodBurnInProtection.registerForView(mLsItemsContainer!!)
aodBurnInProtection!!.setMovementEnabled(true)
applyLayoutConstraints(mLsItemsContainer!!)

// Hide stock clock
Expand Down Expand Up @@ -465,6 +470,26 @@ class LockscreenClockA15(context: Context) : ModPack(context) {
}
}

fun onDozingChanged(isDozing: Boolean) {
aodBurnInProtection?.setMovementEnabled(isDozing)
}

val collapsedStatusBarFragment = findClass(
"$SYSTEMUI_PACKAGE.statusbar.phone.CollapsedStatusBarFragment",
"$SYSTEMUI_PACKAGE.statusbar.phone.fragment.CollapsedStatusBarFragment"
)

collapsedStatusBarFragment
.hookMethod("onDozingChanged")
.runAfter { param -> onDozingChanged(param.args[0] as Boolean) }

val dozeScrimControllerClass =
findClass("$SYSTEMUI_PACKAGE.statusbar.phone.DozeScrimController")

dozeScrimControllerClass
.hookMethod("onDozingChanged")
.runAfter { param -> onDozingChanged(param.args[0] as Boolean) }

// For unknown reason, rotating device makes the height of view to 0
// This is a workaround to make sure the view is visible
fun updateLayoutParams() {
Expand Down Expand Up @@ -512,7 +537,7 @@ class LockscreenClockA15(context: Context) : ModPack(context) {
.hookMethod("onDreamingStarted")
.runAfter {
coroutineScope.launch {
repeat(3) {
repeat(5) {
updateLayoutParams()
delay(500L)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import com.drdisagree.iconify.xposed.modules.extras.utils.toolkit.XposedHook.Com
import com.drdisagree.iconify.xposed.modules.extras.utils.toolkit.getFieldSilently
import com.drdisagree.iconify.xposed.modules.extras.utils.toolkit.hookConstructor
import com.drdisagree.iconify.xposed.modules.extras.utils.toolkit.hookMethod
import com.drdisagree.iconify.xposed.modules.extras.views.AodBurnInProtection
import com.drdisagree.iconify.xposed.modules.extras.views.CurrentWeatherView
import com.drdisagree.iconify.xposed.modules.lockscreen.Lockscreen.Companion.isComposeLockscreen
import com.drdisagree.iconify.xposed.utils.XPrefs.Xprefs
Expand Down Expand Up @@ -93,6 +94,7 @@ class LockscreenWeatherA15(context: Context) : ModPack(context) {
private lateinit var mWeatherContainer: LinearLayout
private val job = Job()
private val coroutineScope = CoroutineScope(Dispatchers.Main + job)
private var aodBurnInProtection: AodBurnInProtection? = null
private var mCustomFontEnabled = false
private val mCustomFontLocation = Environment.getExternalStorageDirectory().toString() +
"/.iconify_files/lockscreen_weather_font.ttf"
Expand Down Expand Up @@ -253,6 +255,9 @@ class LockscreenWeatherA15(context: Context) : ModPack(context) {
}

applyLayoutConstraints(mLsItemsContainer ?: mWeatherContainer)
aodBurnInProtection = AodBurnInProtection.registerForView(
mLsItemsContainer ?: mWeatherContainer
)

placeWeatherView()
}, 1000)
Expand Down Expand Up @@ -372,6 +377,26 @@ class LockscreenWeatherA15(context: Context) : ModPack(context) {
}
}

fun onDozingChanged(isDozing: Boolean) {
aodBurnInProtection?.setMovementEnabled(isDozing)
}

val collapsedStatusBarFragment = findClass(
"$SYSTEMUI_PACKAGE.statusbar.phone.CollapsedStatusBarFragment",
"$SYSTEMUI_PACKAGE.statusbar.phone.fragment.CollapsedStatusBarFragment"
)

collapsedStatusBarFragment
.hookMethod("onDozingChanged")
.runAfter { param -> onDozingChanged(param.args[0] as Boolean) }

val dozeScrimControllerClass =
findClass("$SYSTEMUI_PACKAGE.statusbar.phone.DozeScrimController")

dozeScrimControllerClass
.hookMethod("onDozingChanged")
.runAfter { param -> onDozingChanged(param.args[0] as Boolean) }

// For unknown reason, rotating device makes the height of view to 0
// This is a workaround to make sure the view is visible
fun updateLayoutParams() {
Expand Down Expand Up @@ -421,7 +446,7 @@ class LockscreenWeatherA15(context: Context) : ModPack(context) {
.hookMethod("onDreamingStarted")
.runAfter {
coroutineScope.launch {
repeat(3) {
repeat(5) {
updateLayoutParams()
delay(500L)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import com.drdisagree.iconify.xposed.modules.extras.utils.toolkit.XposedHook.Com
import com.drdisagree.iconify.xposed.modules.extras.utils.toolkit.getFieldSilently
import com.drdisagree.iconify.xposed.modules.extras.utils.toolkit.hookConstructor
import com.drdisagree.iconify.xposed.modules.extras.utils.toolkit.hookMethod
import com.drdisagree.iconify.xposed.modules.extras.views.AodBurnInProtection
import com.drdisagree.iconify.xposed.modules.extras.views.LockscreenWidgetsView
import com.drdisagree.iconify.xposed.modules.lockscreen.Lockscreen.Companion.isComposeLockscreen
import com.drdisagree.iconify.xposed.utils.XPrefs.Xprefs
Expand Down Expand Up @@ -116,6 +117,7 @@ class LockscreenWidgetsA15(context: Context) : ModPack(context) {
private var dateSmartSpaceViewAvailable = false
private val job = Job()
private val coroutineScope = CoroutineScope(Dispatchers.Main + job)
private var aodBurnInProtection: AodBurnInProtection? = null

private var mBroadcastRegistered = false
private val mReceiver: BroadcastReceiver = object : BroadcastReceiver() {
Expand Down Expand Up @@ -314,6 +316,9 @@ class LockscreenWidgetsA15(context: Context) : ModPack(context) {
}

applyLayoutConstraints(mLsItemsContainer ?: mWidgetsContainer)
aodBurnInProtection = AodBurnInProtection.registerForView(
mLsItemsContainer ?: mWidgetsContainer
)

placeWidgetsView()
}, 1000)
Expand Down Expand Up @@ -443,12 +448,26 @@ class LockscreenWidgetsA15(context: Context) : ModPack(context) {
updateLockscreenWidgetsOnClock(param.args[0] as Boolean)
}

fun onDozingChanged(isDozing: Boolean) {
updateDozingState(isDozing)
aodBurnInProtection?.setMovementEnabled(isDozing)
}

val collapsedStatusBarFragment = findClass(
"$SYSTEMUI_PACKAGE.statusbar.phone.CollapsedStatusBarFragment",
"$SYSTEMUI_PACKAGE.statusbar.phone.fragment.CollapsedStatusBarFragment"
)

collapsedStatusBarFragment
.hookMethod("onDozingChanged")
.runAfter { param -> onDozingChanged(param.args[0] as Boolean) }

val dozeScrimControllerClass =
findClass("$SYSTEMUI_PACKAGE.statusbar.phone.DozeScrimController")

dozeScrimControllerClass
.hookMethod("onDozingChanged")
.runAfter { param -> updateDozingState(param.args[0] as Boolean) }
.runAfter { param -> onDozingChanged(param.args[0] as Boolean) }

// For unknown reason, rotating device makes the height of view to 0
// This is a workaround to make sure the view is visible
Expand Down Expand Up @@ -499,7 +518,7 @@ class LockscreenWidgetsA15(context: Context) : ModPack(context) {
.hookMethod("onDreamingStarted")
.runAfter {
coroutineScope.launch {
repeat(3) {
repeat(5) {
updateLayoutParams()
delay(500L)
}
Expand Down

0 comments on commit 3071f0d

Please sign in to comment.