Fix Android 15 AccessibilityService Magnification Resetting on Home Screen
Understanding Magnification Resets in Android 15
When developing an AccessibilityService for low-vision users on Android 15 (API 35), you might encounter an unexpected issue: setting full-screen magnification using AccessibilityService.MagnificationController works within apps, but swiping up to the Home screen immediately resets the zoom scale back to 1.0.
If you try to re-apply the magnification immediately via the listener, the screen zooms in briefly before snapping back to normal size. Let's look at why this happens and how to keep magnification persistent across launcher transitions.
Why Does Android Reset Magnification on the Home Screen?
In modern Android versions (specifically Android 12 through 15), the WindowManager and System UI manage transitions between application tasks and the launcher. During a transition to the Home screen:
- Window Transitions: The navigation gesture triggers an animated transition to the launcher, temporarily altering window hierarchies and resetting transient display magnification configurations.
- System Gestures Conflict: Android separates programmatic magnification from the built-in system accessibility shortcut (e.g., triple-tap or accessibility button). If system magnification isn't actively toggled by the user, the window manager treats programmatic zoom as an active override that it may reset when switching display contexts.
- Race Conditions: Re-applying the zoom instantly inside
onMagnificationChangedresults in a race condition where the launcher transition animation overrides your new scale.
Step-by-Step Solution: Maintaining Zoom Across Navigation
1. Wait for Launcher Window Stabilization
Rather than reapplying magnification immediately when the scale drops to 1.0, listen for window state transitions and apply magnification once the launcher has completely settled.
Add window change monitoring to your service configuration:
<accessibility-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeWindowStateChanged|typeWindowsChanged"
android:accessibilityFeedbackType="feedbackGeneric"
android:canRetrieveWindowContent="true"
android:notificationTimeout="100"
android:canControlMagnification="true" />2. Implement Debounced Zoom Persistence
Create a dedicated helper method to ensure magnification stays set without triggering rapid-fire rebuild loops between your service and the operating system.
import android.accessibilityservice.AccessibilityService
import android.accessibilityservice.MagnificationConfig
import android.os.Handler
import android.os.Looper
import android.view.Display
import android.view.accessibility.AccessibilityEvent
class CustomMagnificationService : AccessibilityService() {
private val handler = Handler(Looper.getMainLooper())
private val targetScale = 1.5f
private var isApplyingConfig = false
override fun onServiceConnected() {
super.onServiceConnected()
applyMagnification(targetScale)
magnificationController.addListener { controller, region, scale, centerX, centerY ->
if (scale == 1.0f && !isApplyingConfig) {
// Debounce re-application to allow window transitions to finish
handler.removeCallbacksAndMessages(null)
handler.postDelayed({ applyMagnification(targetScale) }, 300)
}
}
}
override fun onAccessibilityEvent(event: AccessibilityEvent?) {
if (event?.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
// Re-assert target zoom when transitioning to new packages (like the Home screen)
handler.removeCallbacksAndMessages(null)
handler.postDelayed({ applyMagnification(targetScale) }, 250)
}
}
private fun applyMagnification(scale: Float) {
isApplyingConfig = true
val config = MagnificationConfig.Builder()
.setMode(MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN)
.setScale(scale)
.setActivated(true)
.build()
magnificationController.setMagnificationConfig(config, true)
handler.postDelayed({ isApplyingConfig = false }, 100)
}
override fun onInterrupt() {
handler.removeCallbacksAndMessages(null)
}
}Key Best Practices for Android 14 and 15
- Target Specific Displays: Starting in API 33+, multiple displays are heavily integrated. Ensure your magnification changes are targeted to
Display.DEFAULT_DISPLAYif interacting directly with lower-level window controllers. - Avoid Animation Conflicts: Passing
animate = falseintosetMagnificationConfig(config, false)can prevent transitions from fighting with the Launcher's own gesture animations. - Check System Accessibility Settings: Ensure that system magnification features (like Magnification Shortcut in Settings > Accessibility) are not simultaneously toggling the mode off. Programmatic magnification works best when it acts harmoniously with Android's native accessibility preferences.