mirror of
https://github.com/bitwarden/android.git
synced 2025-12-10 20:07:59 -06:00
[PM-14435] Accessibility enabled settings changes to address older and custom Android phone versions (#4756)
This commit is contained in:
parent
ec030f2c2e
commit
ac6ff98041
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,10 +57,10 @@ object AccessibilityModule {
|
||||
@Singleton
|
||||
@Provides
|
||||
fun providesAccessibilityEnabledManager(
|
||||
accessibilityManager: AccessibilityManager,
|
||||
@ApplicationContext context: Context,
|
||||
): AccessibilityEnabledManager =
|
||||
AccessibilityEnabledManagerImpl(
|
||||
accessibilityManager = accessibilityManager,
|
||||
context = context,
|
||||
)
|
||||
|
||||
@Singleton
|
||||
|
||||
@ -10,4 +10,9 @@ interface AccessibilityEnabledManager {
|
||||
* Emits updates that track whether the accessibility autofill service is enabled..
|
||||
*/
|
||||
val isAccessibilityEnabledStateFlow: StateFlow<Boolean>
|
||||
|
||||
/**
|
||||
* Gets the accessibility enabled state from the system settings.
|
||||
*/
|
||||
fun refreshAccessibilityEnabledFromSettings()
|
||||
}
|
||||
|
||||
@ -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<Boolean>
|
||||
get() = mutableIsAccessibilityEnabledStateFlow.asStateFlow()
|
||||
|
||||
override fun refreshAccessibilityEnabledFromSettings() {
|
||||
mutableIsAccessibilityEnabledStateFlow.value = context.isAccessibilityServiceEnabled
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<AccessibilityManager.AccessibilityStateChangeListener>()
|
||||
private val accessibilityManager = mockk<AccessibilityManager> {
|
||||
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())
|
||||
|
||||
accessibilityStateChangeListener.captured.onAccessibilityStateChanged(true)
|
||||
assertTrue(awaitItem())
|
||||
|
||||
accessibilityStateChangeListener.captured.onAccessibilityStateChanged(true)
|
||||
expectNoEvents()
|
||||
|
||||
accessibilityStateChangeListener.captured.onAccessibilityStateChanged(false)
|
||||
assertFalse(awaitItem())
|
||||
every { context.isAccessibilityServiceEnabled } returns false
|
||||
accessibilityEnabledManager.refreshAccessibilityEnabledFromSettings()
|
||||
val result = accessibilityEnabledManager.isAccessibilityEnabledStateFlow.value
|
||||
assertFalse(result)
|
||||
}
|
||||
|
||||
@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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,6 +11,10 @@ class FakeAccessibilityEnabledManager : AccessibilityEnabledManager {
|
||||
override val isAccessibilityEnabledStateFlow: StateFlow<Boolean>
|
||||
get() = mutableIsAccessibilityEnabledStateFlow.asStateFlow()
|
||||
|
||||
override fun refreshAccessibilityEnabledFromSettings() {
|
||||
mutableIsAccessibilityEnabledStateFlow.value = isAccessibilityEnabled
|
||||
}
|
||||
|
||||
var isAccessibilityEnabled: Boolean
|
||||
get() = mutableIsAccessibilityEnabledStateFlow.value
|
||||
set(value) {
|
||||
|
||||
@ -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()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user