Skip to content

Commit

Permalink
PM-14409: Add realtime check for when the accessibility service is en…
Browse files Browse the repository at this point in the history
…abled or disabled (#4314)
  • Loading branch information
david-livefront authored Nov 15, 2024
1 parent a04598c commit 30eb11b
Show file tree
Hide file tree
Showing 11 changed files with 68 additions and 188 deletions.
4 changes: 0 additions & 4 deletions app/src/main/java/com/x8bit/bitwarden/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import androidx.core.os.LocaleListCompat
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.compose.rememberNavController
import com.x8bit.bitwarden.data.autofill.accessibility.manager.AccessibilityActivityManager
import com.x8bit.bitwarden.data.autofill.accessibility.manager.AccessibilityCompletionManager
import com.x8bit.bitwarden.data.autofill.manager.AutofillActivityManager
import com.x8bit.bitwarden.data.autofill.manager.AutofillCompletionManager
Expand All @@ -39,9 +38,6 @@ class MainActivity : AppCompatActivity() {

private val mainViewModel: MainViewModel by viewModels()

@Inject
lateinit var accessibilityActivityManager: AccessibilityActivityManager

@Inject
lateinit var autofillActivityManager: AutofillActivityManager

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.x8bit.bitwarden.data.autofill.accessibility.di
import android.content.Context
import android.content.pm.PackageManager
import android.os.PowerManager
import android.view.accessibility.AccessibilityManager
import com.x8bit.bitwarden.data.autofill.accessibility.manager.AccessibilityAutofillManager
import com.x8bit.bitwarden.data.autofill.accessibility.manager.AccessibilityAutofillManagerImpl
import com.x8bit.bitwarden.data.autofill.accessibility.manager.AccessibilityCompletionManager
Expand Down Expand Up @@ -55,8 +56,12 @@ object AccessibilityModule {

@Singleton
@Provides
fun providesAccessibilityEnabledManager(): AccessibilityEnabledManager =
AccessibilityEnabledManagerImpl()
fun providesAccessibilityEnabledManager(
accessibilityManager: AccessibilityManager,
): AccessibilityEnabledManager =
AccessibilityEnabledManagerImpl(
accessibilityManager = accessibilityManager,
)

@Singleton
@Provides
Expand Down Expand Up @@ -110,6 +115,12 @@ object AccessibilityModule {
@ApplicationContext context: Context,
): PackageManager = context.packageManager

@Singleton
@Provides
fun provideAccessibilityManager(
@ApplicationContext context: Context,
): AccessibilityManager = context.getSystemService(AccessibilityManager::class.java)

@Singleton
@Provides
fun providesPowerManager(
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,7 @@ import kotlinx.coroutines.flow.StateFlow
*/
interface AccessibilityEnabledManager {
/**
* Whether or not the accessibility service should be considered enabled.
*
* Note that changing this does not enable or disable autofill; it is only an indicator that
* this has occurred elsewhere.
*/
var isAccessibilityEnabled: Boolean

/**
* Emits updates that track [isAccessibilityEnabled] values.
* Emits updates that track whether the accessibility autofill service is enabled..
*/
val isAccessibilityEnabledStateFlow: StateFlow<Boolean>
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
package com.x8bit.bitwarden.data.autofill.accessibility.manager

import android.view.accessibility.AccessibilityManager
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow

/**
* The default implementation of [AccessibilityEnabledManager].
*/
class AccessibilityEnabledManagerImpl : AccessibilityEnabledManager {
class AccessibilityEnabledManagerImpl(
accessibilityManager: AccessibilityManager,
) : AccessibilityEnabledManager {
private val mutableIsAccessibilityEnabledStateFlow = MutableStateFlow(value = false)

override var isAccessibilityEnabled: Boolean
get() = mutableIsAccessibilityEnabledStateFlow.value
set(value) {
mutableIsAccessibilityEnabledStateFlow.value = value
}
init {
accessibilityManager.addAccessibilityStateChangeListener(
AccessibilityManager.AccessibilityStateChangeListener { isEnabled ->
mutableIsAccessibilityEnabledStateFlow.value = isEnabled
},
)
}

override val isAccessibilityEnabledStateFlow: StateFlow<Boolean>
get() = mutableIsAccessibilityEnabledStateFlow.asStateFlow()
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,30 +1,44 @@
package com.x8bit.bitwarden.data.autofill.accessibility.manager

import android.view.accessibility.AccessibilityManager
import app.cash.turbine.test
import io.mockk.every
import io.mockk.mockk
import io.mockk.slot
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test

class AccessibilityEnabledManagerTest {

private val accessibilityStateChangeListener =
slot<AccessibilityManager.AccessibilityStateChangeListener>()
private val accessibilityManager = mockk<AccessibilityManager> {
every {
addAccessibilityStateChangeListener(capture(accessibilityStateChangeListener))
} returns true
}

private val accessibilityEnabledManager: AccessibilityEnabledManager =
AccessibilityEnabledManagerImpl()
AccessibilityEnabledManagerImpl(
accessibilityManager = accessibilityManager,
)

@Suppress("MaxLineLength")
@Test
fun `isAccessibilityEnabledStateFlow should emit whenever isAccessibilityEnabled is set to a unique value`() =
fun `isAccessibilityEnabledStateFlow should emit whenever accessibilityStateChangeListener emits a unique value`() =
runTest {
accessibilityEnabledManager.isAccessibilityEnabledStateFlow.test {
assertFalse(awaitItem())

accessibilityEnabledManager.isAccessibilityEnabled = true
accessibilityStateChangeListener.captured.onAccessibilityStateChanged(true)
assertTrue(awaitItem())

accessibilityEnabledManager.isAccessibilityEnabled = true
accessibilityStateChangeListener.captured.onAccessibilityStateChanged(true)
expectNoEvents()

accessibilityEnabledManager.isAccessibilityEnabled = false
accessibilityStateChangeListener.captured.onAccessibilityStateChanged(false)
assertFalse(awaitItem())
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.x8bit.bitwarden.data.autofill.accessibility.manager

import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow

class FakeAccessibilityEnabledManager : AccessibilityEnabledManager {

private val mutableIsAccessibilityEnabledStateFlow = MutableStateFlow(value = false)

override val isAccessibilityEnabledStateFlow: StateFlow<Boolean>
get() = mutableIsAccessibilityEnabledStateFlow.asStateFlow()

var isAccessibilityEnabled: Boolean
get() = mutableIsAccessibilityEnabledStateFlow.value
set(value) {
mutableIsAccessibilityEnabledStateFlow.value = value
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ import com.x8bit.bitwarden.data.auth.datasource.network.model.KdfTypeJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.TrustedDeviceUserDecryptionOptionsJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.UserDecryptionOptionsJson
import com.x8bit.bitwarden.data.auth.repository.model.UserFingerprintResult
import com.x8bit.bitwarden.data.autofill.accessibility.manager.AccessibilityEnabledManager
import com.x8bit.bitwarden.data.autofill.accessibility.manager.AccessibilityEnabledManagerImpl
import com.x8bit.bitwarden.data.autofill.accessibility.manager.FakeAccessibilityEnabledManager
import com.x8bit.bitwarden.data.autofill.manager.AutofillEnabledManager
import com.x8bit.bitwarden.data.autofill.manager.AutofillEnabledManagerImpl
import com.x8bit.bitwarden.data.platform.base.FakeDispatcherManager
Expand Down Expand Up @@ -55,8 +54,7 @@ class SettingsRepositoryTest {
every { disableAutofillServices() } just runs
}
private val autofillEnabledManager: AutofillEnabledManager = AutofillEnabledManagerImpl()
private val accessibilityEnabledManager: AccessibilityEnabledManager =
AccessibilityEnabledManagerImpl()
private val fakeAccessibilityEnabledManager = FakeAccessibilityEnabledManager()
private val fakeAuthDiskSource = FakeAuthDiskSource()
private val fakeSettingsDiskSource = FakeSettingsDiskSource()
private val vaultSdkSource: VaultSdkSource = mockk()
Expand All @@ -75,7 +73,7 @@ class SettingsRepositoryTest {
settingsDiskSource = fakeSettingsDiskSource,
vaultSdkSource = vaultSdkSource,
biometricsEncryptionManager = biometricsEncryptionManager,
accessibilityEnabledManager = accessibilityEnabledManager,
accessibilityEnabledManager = fakeAccessibilityEnabledManager,
dispatcherManager = FakeDispatcherManager(),
policyManager = policyManager,
)
Expand Down Expand Up @@ -691,10 +689,10 @@ class SettingsRepositoryTest {
settingsRepository.isAccessibilityEnabledStateFlow.test {
assertFalse(awaitItem())

accessibilityEnabledManager.isAccessibilityEnabled = true
fakeAccessibilityEnabledManager.isAccessibilityEnabled = true
assertTrue(awaitItem())

accessibilityEnabledManager.isAccessibilityEnabled = false
fakeAccessibilityEnabledManager.isAccessibilityEnabled = false
assertFalse(awaitItem())
}
}
Expand Down

0 comments on commit 30eb11b

Please sign in to comment.