Skip to content

Commit ac6ff98

Browse files
authored
[PM-14435] Accessibility enabled settings changes to address older and custom Android phone versions (#4756)
1 parent ec030f2 commit ac6ff98

File tree

7 files changed

+67
-38
lines changed

7 files changed

+67
-38
lines changed

app/src/main/java/com/x8bit/bitwarden/data/autofill/accessibility/BitwardenAccessibilityService.kt

+16
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package com.x8bit.bitwarden.data.autofill.accessibility
22

33
import android.accessibilityservice.AccessibilityService
4+
import android.content.Intent
45
import android.view.accessibility.AccessibilityEvent
56
import androidx.annotation.Keep
7+
import com.x8bit.bitwarden.data.autofill.accessibility.manager.AccessibilityEnabledManager
68
import com.x8bit.bitwarden.data.autofill.accessibility.processor.BitwardenAccessibilityProcessor
79
import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
810
import com.x8bit.bitwarden.data.tiles.BitwardenAutofillTileService
@@ -21,9 +23,23 @@ class BitwardenAccessibilityService : AccessibilityService() {
2123
@Inject
2224
lateinit var processor: BitwardenAccessibilityProcessor
2325

26+
@Inject
27+
lateinit var accessibilityEnabledManager: AccessibilityEnabledManager
28+
2429
override fun onAccessibilityEvent(event: AccessibilityEvent) {
2530
processor.processAccessibilityEvent(event = event) { rootInActiveWindow }
2631
}
2732

2833
override fun onInterrupt() = Unit
34+
35+
override fun onUnbind(intent: Intent?): Boolean {
36+
return super
37+
.onUnbind(intent)
38+
.also { accessibilityEnabledManager.refreshAccessibilityEnabledFromSettings() }
39+
}
40+
41+
override fun onServiceConnected() {
42+
super.onServiceConnected()
43+
accessibilityEnabledManager.refreshAccessibilityEnabledFromSettings()
44+
}
2945
}

app/src/main/java/com/x8bit/bitwarden/data/autofill/accessibility/di/AccessibilityModule.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,10 @@ object AccessibilityModule {
5757
@Singleton
5858
@Provides
5959
fun providesAccessibilityEnabledManager(
60-
accessibilityManager: AccessibilityManager,
60+
@ApplicationContext context: Context,
6161
): AccessibilityEnabledManager =
6262
AccessibilityEnabledManagerImpl(
63-
accessibilityManager = accessibilityManager,
63+
context = context,
6464
)
6565

6666
@Singleton

app/src/main/java/com/x8bit/bitwarden/data/autofill/accessibility/manager/AccessibilityEnabledManager.kt

+5
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,9 @@ interface AccessibilityEnabledManager {
1010
* Emits updates that track whether the accessibility autofill service is enabled..
1111
*/
1212
val isAccessibilityEnabledStateFlow: StateFlow<Boolean>
13+
14+
/**
15+
* Gets the accessibility enabled state from the system settings.
16+
*/
17+
fun refreshAccessibilityEnabledFromSettings()
1318
}
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.x8bit.bitwarden.data.autofill.accessibility.manager
22

3-
import android.view.accessibility.AccessibilityManager
3+
import android.content.Context
4+
import com.x8bit.bitwarden.data.autofill.accessibility.util.isAccessibilityServiceEnabled
45
import kotlinx.coroutines.flow.MutableStateFlow
56
import kotlinx.coroutines.flow.StateFlow
67
import kotlinx.coroutines.flow.asStateFlow
@@ -9,20 +10,20 @@ import kotlinx.coroutines.flow.asStateFlow
910
* The default implementation of [AccessibilityEnabledManager].
1011
*/
1112
class AccessibilityEnabledManagerImpl(
12-
accessibilityManager: AccessibilityManager,
13+
private val context: Context,
1314
) : AccessibilityEnabledManager {
1415
private val mutableIsAccessibilityEnabledStateFlow = MutableStateFlow(
15-
value = accessibilityManager.isEnabled,
16+
value = context.isAccessibilityServiceEnabled,
1617
)
1718

1819
init {
19-
accessibilityManager.addAccessibilityStateChangeListener(
20-
AccessibilityManager.AccessibilityStateChangeListener { isEnabled ->
21-
mutableIsAccessibilityEnabledStateFlow.value = isEnabled
22-
},
23-
)
20+
mutableIsAccessibilityEnabledStateFlow.value = context.isAccessibilityServiceEnabled
2421
}
2522

2623
override val isAccessibilityEnabledStateFlow: StateFlow<Boolean>
2724
get() = mutableIsAccessibilityEnabledStateFlow.asStateFlow()
25+
26+
override fun refreshAccessibilityEnabledFromSettings() {
27+
mutableIsAccessibilityEnabledStateFlow.value = context.isAccessibilityServiceEnabled
28+
}
2829
}
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,50 @@
11
package com.x8bit.bitwarden.data.autofill.accessibility.manager
22

3-
import android.view.accessibility.AccessibilityManager
4-
import app.cash.turbine.test
3+
import android.content.Context
4+
import com.x8bit.bitwarden.data.autofill.accessibility.util.isAccessibilityServiceEnabled
55
import io.mockk.every
66
import io.mockk.mockk
7-
import io.mockk.slot
7+
import io.mockk.mockkStatic
8+
import io.mockk.unmockkAll
89
import kotlinx.coroutines.test.runTest
10+
import org.junit.jupiter.api.AfterEach
911
import org.junit.jupiter.api.Assertions.assertFalse
1012
import org.junit.jupiter.api.Assertions.assertTrue
13+
import org.junit.jupiter.api.BeforeEach
1114
import org.junit.jupiter.api.Test
1215

1316
class AccessibilityEnabledManagerTest {
17+
private val context: Context = mockk()
1418

15-
private val accessibilityStateChangeListener =
16-
slot<AccessibilityManager.AccessibilityStateChangeListener>()
17-
private val accessibilityManager = mockk<AccessibilityManager> {
18-
every { isEnabled } returns false
19-
every {
20-
addAccessibilityStateChangeListener(capture(accessibilityStateChangeListener))
21-
} returns true
19+
private lateinit var accessibilityEnabledManager: AccessibilityEnabledManager
20+
21+
@BeforeEach
22+
fun setUp() {
23+
mockkStatic(Context::isAccessibilityServiceEnabled)
24+
every { context.isAccessibilityServiceEnabled } returns false
25+
accessibilityEnabledManager = AccessibilityEnabledManagerImpl(context)
2226
}
2327

24-
private val accessibilityEnabledManager: AccessibilityEnabledManager =
25-
AccessibilityEnabledManagerImpl(
26-
accessibilityManager = accessibilityManager,
27-
)
28+
@AfterEach
29+
fun tearDown() {
30+
unmockkAll()
31+
}
2832

29-
@Suppress("MaxLineLength")
3033
@Test
31-
fun `isAccessibilityEnabledStateFlow should emit whenever accessibilityStateChangeListener emits a unique value`() =
34+
fun `isAccessibilityEnabled returns false when setting does not contain our service`() =
3235
runTest {
33-
accessibilityEnabledManager.isAccessibilityEnabledStateFlow.test {
34-
assertFalse(awaitItem())
35-
36-
accessibilityStateChangeListener.captured.onAccessibilityStateChanged(true)
37-
assertTrue(awaitItem())
38-
39-
accessibilityStateChangeListener.captured.onAccessibilityStateChanged(true)
40-
expectNoEvents()
36+
every { context.isAccessibilityServiceEnabled } returns false
37+
accessibilityEnabledManager.refreshAccessibilityEnabledFromSettings()
38+
val result = accessibilityEnabledManager.isAccessibilityEnabledStateFlow.value
39+
assertFalse(result)
40+
}
4141

42-
accessibilityStateChangeListener.captured.onAccessibilityStateChanged(false)
43-
assertFalse(awaitItem())
44-
}
42+
@Test
43+
fun `isAccessibilityEnabled returns true when setting contains the defined service`() =
44+
runTest {
45+
every { context.isAccessibilityServiceEnabled } returns true
46+
accessibilityEnabledManager.refreshAccessibilityEnabledFromSettings()
47+
val result = accessibilityEnabledManager.isAccessibilityEnabledStateFlow.value
48+
assertTrue(result)
4549
}
4650
}

app/src/test/java/com/x8bit/bitwarden/data/autofill/accessibility/manager/FakeAccessibilityEnabledManager.kt

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ class FakeAccessibilityEnabledManager : AccessibilityEnabledManager {
1111
override val isAccessibilityEnabledStateFlow: StateFlow<Boolean>
1212
get() = mutableIsAccessibilityEnabledStateFlow.asStateFlow()
1313

14+
override fun refreshAccessibilityEnabledFromSettings() {
15+
mutableIsAccessibilityEnabledStateFlow.value = isAccessibilityEnabled
16+
}
17+
1418
var isAccessibilityEnabled: Boolean
1519
get() = mutableIsAccessibilityEnabledStateFlow.value
1620
set(value) {

app/src/test/java/com/x8bit/bitwarden/data/platform/manager/ReviewPromptManagerTest.kt

-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import org.junit.jupiter.api.Assertions.assertTrue
1414
import org.junit.jupiter.api.Test
1515

1616
class ReviewPromptManagerTest {
17-
1817
private val autofillEnabledManager: AutofillEnabledManager = AutofillEnabledManagerImpl()
1918
private val fakeAccessibilityEnabledManager = FakeAccessibilityEnabledManager()
2019
private val fakeAuthDiskSource = FakeAuthDiskSource()

0 commit comments

Comments
 (0)