diff --git a/app/src/debug/AndroidManifest.xml b/app/src/debug/AndroidManifest.xml index e1b3fd96c..f55d7cf5d 100644 --- a/app/src/debug/AndroidManifest.xml +++ b/app/src/debug/AndroidManifest.xml @@ -3,6 +3,24 @@ xmlns:tools="http://schemas.android.com/tools"> + + + + + + + + + + { + super.instantiateServiceCompat( + cl, + BitwardenAutofillTileService::class.java.name, + intent, + ) + } + LEGACY_CREDENTIAL_SERVICE_NAME -> { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { super.instantiateServiceCompat( diff --git a/app/src/main/java/com/x8bit/bitwarden/data/accessibility/di/AccessibilityModule.kt b/app/src/main/java/com/x8bit/bitwarden/data/accessibility/di/AccessibilityModule.kt new file mode 100644 index 000000000..d5f0bc283 --- /dev/null +++ b/app/src/main/java/com/x8bit/bitwarden/data/accessibility/di/AccessibilityModule.kt @@ -0,0 +1,21 @@ +package com.x8bit.bitwarden.data.accessibility.di + +import com.x8bit.bitwarden.data.accessibility.manager.AccessibilityAutofillManager +import com.x8bit.bitwarden.data.accessibility.manager.AccessibilityAutofillManagerImpl +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +/** + * Provides dependencies within the accessibility package. + */ +@Module +@InstallIn(SingletonComponent::class) +object AccessibilityModule { + @Singleton + @Provides + fun providesAccessibilityInvokeManager(): AccessibilityAutofillManager = + AccessibilityAutofillManagerImpl() +} diff --git a/app/src/main/java/com/x8bit/bitwarden/data/accessibility/manager/AccessibilityAutofillManager.kt b/app/src/main/java/com/x8bit/bitwarden/data/accessibility/manager/AccessibilityAutofillManager.kt new file mode 100644 index 000000000..4423e55df --- /dev/null +++ b/app/src/main/java/com/x8bit/bitwarden/data/accessibility/manager/AccessibilityAutofillManager.kt @@ -0,0 +1,12 @@ +package com.x8bit.bitwarden.data.accessibility.manager + +/** + * A relay manager used to notify the accessibility service to attempt an autofill. + */ +interface AccessibilityAutofillManager { + /** + * Indicates that the Autofill tile has been clicked and we attempt an accessibility-based + * autofill. + */ + var isAccessibilityTileClicked: Boolean +} diff --git a/app/src/main/java/com/x8bit/bitwarden/data/accessibility/manager/AccessibilityAutofillManagerImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/accessibility/manager/AccessibilityAutofillManagerImpl.kt new file mode 100644 index 000000000..1d55a1208 --- /dev/null +++ b/app/src/main/java/com/x8bit/bitwarden/data/accessibility/manager/AccessibilityAutofillManagerImpl.kt @@ -0,0 +1,8 @@ +package com.x8bit.bitwarden.data.accessibility.manager + +/** + * The default implementation for the [AccessibilityAutofillManager]. + */ +class AccessibilityAutofillManagerImpl : AccessibilityAutofillManager { + override var isAccessibilityTileClicked: Boolean = false +} diff --git a/app/src/main/java/com/x8bit/bitwarden/data/tiles/BitwardenAutofillTileService.kt b/app/src/main/java/com/x8bit/bitwarden/data/tiles/BitwardenAutofillTileService.kt new file mode 100644 index 000000000..45b95cf14 --- /dev/null +++ b/app/src/main/java/com/x8bit/bitwarden/data/tiles/BitwardenAutofillTileService.kt @@ -0,0 +1,52 @@ +package com.x8bit.bitwarden.data.tiles + +import android.annotation.SuppressLint +import android.app.PendingIntent +import android.content.Intent +import android.os.Build +import android.service.quicksettings.TileService +import androidx.annotation.Keep +import com.x8bit.bitwarden.AccessibilityActivity +import com.x8bit.bitwarden.data.accessibility.manager.AccessibilityAutofillManager +import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage +import com.x8bit.bitwarden.data.platform.util.isBuildVersionBelow +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject + +/** + * A service for handling the Autofill quick settings tile. + */ +@AndroidEntryPoint +@Keep +@OmitFromCoverage +class BitwardenAutofillTileService : TileService() { + @Inject + lateinit var accessibilityAutofillManager: AccessibilityAutofillManager + + override fun onClick() { + if (isLocked) { + unlockAndRun { launchAutofill() } + } else { + launchAutofill() + } + } + + @SuppressLint("StartActivityAndCollapseDeprecated") + private fun launchAutofill() { + accessibilityAutofillManager.isAccessibilityTileClicked = true + val intent = Intent(applicationContext, AccessibilityActivity::class.java) + if (isBuildVersionBelow(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)) { + @Suppress("DEPRECATION") + startActivityAndCollapse(intent) + } else { + startActivityAndCollapse( + PendingIntent.getActivity( + applicationContext, + 0, + intent, + PendingIntent.FLAG_IMMUTABLE, + ), + ) + } + } +} diff --git a/app/src/test/java/com/x8bit/bitwarden/data/accessibility/manager/AccessibilityAutofillManagerTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/accessibility/manager/AccessibilityAutofillManagerTest.kt new file mode 100644 index 000000000..23a753a19 --- /dev/null +++ b/app/src/test/java/com/x8bit/bitwarden/data/accessibility/manager/AccessibilityAutofillManagerTest.kt @@ -0,0 +1,19 @@ +package com.x8bit.bitwarden.data.accessibility.manager + +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test + +class AccessibilityAutofillManagerTest { + private val accessibilityAutofillManager: AccessibilityAutofillManager = + AccessibilityAutofillManagerImpl() + + @Test + fun `isAccessibilityTileClicked should simply hold the state it is provided`() { + assertFalse(accessibilityAutofillManager.isAccessibilityTileClicked) + accessibilityAutofillManager.isAccessibilityTileClicked = true + assertTrue(accessibilityAutofillManager.isAccessibilityTileClicked) + accessibilityAutofillManager.isAccessibilityTileClicked = false + assertFalse(accessibilityAutofillManager.isAccessibilityTileClicked) + } +}