From ac6ff98041b97881a4da2fbc8e072c544f20bfb0 Mon Sep 17 00:00:00 2001 From: aj-rosado <109146700+aj-rosado@users.noreply.github.com> Date: Fri, 28 Feb 2025 22:25:11 +0000 Subject: [PATCH] [PM-14435] Accessibility enabled settings changes to address older and custom Android phone versions (#4756) --- .../BitwardenAccessibilityService.kt | 16 +++++ .../accessibility/di/AccessibilityModule.kt | 4 +- .../manager/AccessibilityEnabledManager.kt | 5 ++ .../AccessibilityEnabledManagerImpl.kt | 17 +++--- .../AccessibilityEnabledManagerTest.kt | 58 ++++++++++--------- .../FakeAccessibilityEnabledManager.kt | 4 ++ .../manager/ReviewPromptManagerTest.kt | 1 - 7 files changed, 67 insertions(+), 38 deletions(-) diff --git a/app/src/main/java/com/x8bit/bitwarden/data/autofill/accessibility/BitwardenAccessibilityService.kt b/app/src/main/java/com/x8bit/bitwarden/data/autofill/accessibility/BitwardenAccessibilityService.kt index 1f0911d57f..25271e99ed 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/autofill/accessibility/BitwardenAccessibilityService.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/autofill/accessibility/BitwardenAccessibilityService.kt @@ -1,8 +1,10 @@ package com.x8bit.bitwarden.data.autofill.accessibility import android.accessibilityservice.AccessibilityService +import android.content.Intent import android.view.accessibility.AccessibilityEvent import androidx.annotation.Keep +import com.x8bit.bitwarden.data.autofill.accessibility.manager.AccessibilityEnabledManager import com.x8bit.bitwarden.data.autofill.accessibility.processor.BitwardenAccessibilityProcessor import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage import com.x8bit.bitwarden.data.tiles.BitwardenAutofillTileService @@ -21,9 +23,23 @@ class BitwardenAccessibilityService : AccessibilityService() { @Inject lateinit var processor: BitwardenAccessibilityProcessor + @Inject + lateinit var accessibilityEnabledManager: AccessibilityEnabledManager + override fun onAccessibilityEvent(event: AccessibilityEvent) { processor.processAccessibilityEvent(event = event) { rootInActiveWindow } } override fun onInterrupt() = Unit + + override fun onUnbind(intent: Intent?): Boolean { + return super + .onUnbind(intent) + .also { accessibilityEnabledManager.refreshAccessibilityEnabledFromSettings() } + } + + override fun onServiceConnected() { + super.onServiceConnected() + accessibilityEnabledManager.refreshAccessibilityEnabledFromSettings() + } } diff --git a/app/src/main/java/com/x8bit/bitwarden/data/autofill/accessibility/di/AccessibilityModule.kt b/app/src/main/java/com/x8bit/bitwarden/data/autofill/accessibility/di/AccessibilityModule.kt index ceed9f19f3..750399e269 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/autofill/accessibility/di/AccessibilityModule.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/autofill/accessibility/di/AccessibilityModule.kt @@ -57,10 +57,10 @@ object AccessibilityModule { @Singleton @Provides fun providesAccessibilityEnabledManager( - accessibilityManager: AccessibilityManager, + @ApplicationContext context: Context, ): AccessibilityEnabledManager = AccessibilityEnabledManagerImpl( - accessibilityManager = accessibilityManager, + context = context, ) @Singleton diff --git a/app/src/main/java/com/x8bit/bitwarden/data/autofill/accessibility/manager/AccessibilityEnabledManager.kt b/app/src/main/java/com/x8bit/bitwarden/data/autofill/accessibility/manager/AccessibilityEnabledManager.kt index 5c498c347f..8408f79844 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/autofill/accessibility/manager/AccessibilityEnabledManager.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/autofill/accessibility/manager/AccessibilityEnabledManager.kt @@ -10,4 +10,9 @@ interface AccessibilityEnabledManager { * Emits updates that track whether the accessibility autofill service is enabled.. */ val isAccessibilityEnabledStateFlow: StateFlow + + /** + * Gets the accessibility enabled state from the system settings. + */ + fun refreshAccessibilityEnabledFromSettings() } diff --git a/app/src/main/java/com/x8bit/bitwarden/data/autofill/accessibility/manager/AccessibilityEnabledManagerImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/autofill/accessibility/manager/AccessibilityEnabledManagerImpl.kt index 7dff18459f..937b9308b9 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/autofill/accessibility/manager/AccessibilityEnabledManagerImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/autofill/accessibility/manager/AccessibilityEnabledManagerImpl.kt @@ -1,6 +1,7 @@ package com.x8bit.bitwarden.data.autofill.accessibility.manager -import android.view.accessibility.AccessibilityManager +import android.content.Context +import com.x8bit.bitwarden.data.autofill.accessibility.util.isAccessibilityServiceEnabled import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -9,20 +10,20 @@ import kotlinx.coroutines.flow.asStateFlow * The default implementation of [AccessibilityEnabledManager]. */ class AccessibilityEnabledManagerImpl( - accessibilityManager: AccessibilityManager, + private val context: Context, ) : AccessibilityEnabledManager { private val mutableIsAccessibilityEnabledStateFlow = MutableStateFlow( - value = accessibilityManager.isEnabled, + value = context.isAccessibilityServiceEnabled, ) init { - accessibilityManager.addAccessibilityStateChangeListener( - AccessibilityManager.AccessibilityStateChangeListener { isEnabled -> - mutableIsAccessibilityEnabledStateFlow.value = isEnabled - }, - ) + mutableIsAccessibilityEnabledStateFlow.value = context.isAccessibilityServiceEnabled } override val isAccessibilityEnabledStateFlow: StateFlow get() = mutableIsAccessibilityEnabledStateFlow.asStateFlow() + + override fun refreshAccessibilityEnabledFromSettings() { + mutableIsAccessibilityEnabledStateFlow.value = context.isAccessibilityServiceEnabled + } } diff --git a/app/src/test/java/com/x8bit/bitwarden/data/autofill/accessibility/manager/AccessibilityEnabledManagerTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/autofill/accessibility/manager/AccessibilityEnabledManagerTest.kt index c68b850e9f..b898947b80 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/autofill/accessibility/manager/AccessibilityEnabledManagerTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/autofill/accessibility/manager/AccessibilityEnabledManagerTest.kt @@ -1,46 +1,50 @@ package com.x8bit.bitwarden.data.autofill.accessibility.manager -import android.view.accessibility.AccessibilityManager -import app.cash.turbine.test +import android.content.Context +import com.x8bit.bitwarden.data.autofill.accessibility.util.isAccessibilityServiceEnabled import io.mockk.every import io.mockk.mockk -import io.mockk.slot +import io.mockk.mockkStatic +import io.mockk.unmockkAll import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test class AccessibilityEnabledManagerTest { + private val context: Context = mockk() - private val accessibilityStateChangeListener = - slot() - private val accessibilityManager = mockk { - every { isEnabled } returns false - every { - addAccessibilityStateChangeListener(capture(accessibilityStateChangeListener)) - } returns true + private lateinit var accessibilityEnabledManager: AccessibilityEnabledManager + + @BeforeEach + fun setUp() { + mockkStatic(Context::isAccessibilityServiceEnabled) + every { context.isAccessibilityServiceEnabled } returns false + accessibilityEnabledManager = AccessibilityEnabledManagerImpl(context) } - private val accessibilityEnabledManager: AccessibilityEnabledManager = - AccessibilityEnabledManagerImpl( - accessibilityManager = accessibilityManager, - ) + @AfterEach + fun tearDown() { + unmockkAll() + } - @Suppress("MaxLineLength") @Test - fun `isAccessibilityEnabledStateFlow should emit whenever accessibilityStateChangeListener emits a unique value`() = + fun `isAccessibilityEnabled returns false when setting does not contain our service`() = runTest { - accessibilityEnabledManager.isAccessibilityEnabledStateFlow.test { - assertFalse(awaitItem()) + every { context.isAccessibilityServiceEnabled } returns false + accessibilityEnabledManager.refreshAccessibilityEnabledFromSettings() + val result = accessibilityEnabledManager.isAccessibilityEnabledStateFlow.value + assertFalse(result) + } - accessibilityStateChangeListener.captured.onAccessibilityStateChanged(true) - assertTrue(awaitItem()) - - accessibilityStateChangeListener.captured.onAccessibilityStateChanged(true) - expectNoEvents() - - accessibilityStateChangeListener.captured.onAccessibilityStateChanged(false) - assertFalse(awaitItem()) - } + @Test + fun `isAccessibilityEnabled returns true when setting contains the defined service`() = + runTest { + every { context.isAccessibilityServiceEnabled } returns true + accessibilityEnabledManager.refreshAccessibilityEnabledFromSettings() + val result = accessibilityEnabledManager.isAccessibilityEnabledStateFlow.value + assertTrue(result) } } diff --git a/app/src/test/java/com/x8bit/bitwarden/data/autofill/accessibility/manager/FakeAccessibilityEnabledManager.kt b/app/src/test/java/com/x8bit/bitwarden/data/autofill/accessibility/manager/FakeAccessibilityEnabledManager.kt index d35d1dab1c..264de55192 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/autofill/accessibility/manager/FakeAccessibilityEnabledManager.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/autofill/accessibility/manager/FakeAccessibilityEnabledManager.kt @@ -11,6 +11,10 @@ class FakeAccessibilityEnabledManager : AccessibilityEnabledManager { override val isAccessibilityEnabledStateFlow: StateFlow get() = mutableIsAccessibilityEnabledStateFlow.asStateFlow() + override fun refreshAccessibilityEnabledFromSettings() { + mutableIsAccessibilityEnabledStateFlow.value = isAccessibilityEnabled + } + var isAccessibilityEnabled: Boolean get() = mutableIsAccessibilityEnabledStateFlow.value set(value) { diff --git a/app/src/test/java/com/x8bit/bitwarden/data/platform/manager/ReviewPromptManagerTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/platform/manager/ReviewPromptManagerTest.kt index 3f98ef7a10..262cd702cd 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/platform/manager/ReviewPromptManagerTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/platform/manager/ReviewPromptManagerTest.kt @@ -14,7 +14,6 @@ import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test class ReviewPromptManagerTest { - private val autofillEnabledManager: AutofillEnabledManager = AutofillEnabledManagerImpl() private val fakeAccessibilityEnabledManager = FakeAccessibilityEnabledManager() private val fakeAuthDiskSource = FakeAuthDiskSource()