mirror of
https://github.com/bitwarden/android.git
synced 2026-06-06 22:42:58 -05:00
BIT-1316: Add compatibility for browser apps (#796)
This commit is contained in:
committed by
Álison Fernandes
parent
0818638273
commit
f2d90dda55
@@ -11,17 +11,11 @@ sealed class AutofillView {
|
||||
* The data important to a given [AutofillView].
|
||||
*
|
||||
* @param autofillId The [AutofillId] associated with this view.
|
||||
* @param idPackage The package id for this view, if there is one.
|
||||
* @param isFocused Whether the view is currently focused.
|
||||
* @param webDomain The web domain for this view, if there is one. (example: m.facebook.com)
|
||||
* @param webScheme The web scheme for this view, if there is one. (example: https)
|
||||
*/
|
||||
data class Data(
|
||||
val autofillId: AutofillId,
|
||||
val idPackage: String?,
|
||||
val isFocused: Boolean,
|
||||
val webDomain: String?,
|
||||
val webScheme: String?,
|
||||
)
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,8 +4,15 @@ import android.view.autofill.AutofillId
|
||||
|
||||
/**
|
||||
* A convenience data structure for view node traversal.
|
||||
*
|
||||
* @param autofillViews The list of views we care about for autofilling.
|
||||
* @param idPackage The package id for this view, if there is one.
|
||||
* @param ignoreAutofillIds The list of [AutofillId]s that should be ignored in the fill response.
|
||||
* @param website The website that is being displayed in the app, given there is one.
|
||||
*/
|
||||
data class ViewNodeTraversalData(
|
||||
val autofillViews: List<AutofillView>,
|
||||
val idPackage: String?,
|
||||
val ignoreAutofillIds: List<AutofillId>,
|
||||
val website: String?,
|
||||
)
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.x8bit.bitwarden.data.autofill.util.buildUriOrNull
|
||||
import com.x8bit.bitwarden.data.autofill.util.getInlinePresentationSpecs
|
||||
import com.x8bit.bitwarden.data.autofill.util.getMaxInlineSuggestionsCount
|
||||
import com.x8bit.bitwarden.data.autofill.util.toAutofillView
|
||||
import com.x8bit.bitwarden.data.autofill.util.website
|
||||
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
|
||||
|
||||
/**
|
||||
@@ -119,6 +120,8 @@ private fun AssistStructure.ViewNode.traverse(): ViewNodeTraversalData {
|
||||
// Set up mutable lists for collecting valid AutofillViews and ignorable view ids.
|
||||
val mutableAutofillViewList: MutableList<AutofillView> = mutableListOf()
|
||||
val mutableIgnoreAutofillIdList: MutableList<AutofillId> = mutableListOf()
|
||||
var idPackage: String? = this.idPackage
|
||||
var website: String? = this.website
|
||||
|
||||
// Try converting this `ViewNode` into an `AutofillView`. If a valid instance is returned, add
|
||||
// it to the list. Otherwise, ignore the `AutofillId` associated with this `ViewNode`.
|
||||
@@ -134,6 +137,15 @@ private fun AssistStructure.ViewNode.traverse(): ViewNodeTraversalData {
|
||||
.let { viewNodeTraversalData ->
|
||||
viewNodeTraversalData.autofillViews.forEach(mutableAutofillViewList::add)
|
||||
viewNodeTraversalData.ignoreAutofillIds.forEach(mutableIgnoreAutofillIdList::add)
|
||||
|
||||
// Get the first non-null idPackage.
|
||||
if (idPackage.isNullOrBlank()) {
|
||||
idPackage = viewNodeTraversalData.idPackage
|
||||
}
|
||||
// Get the first non-null website.
|
||||
if (website == null) {
|
||||
website = viewNodeTraversalData.website
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,6 +153,8 @@ private fun AssistStructure.ViewNode.traverse(): ViewNodeTraversalData {
|
||||
// descendant's.
|
||||
return ViewNodeTraversalData(
|
||||
autofillViews = mutableAutofillViewList,
|
||||
idPackage = idPackage,
|
||||
ignoreAutofillIds = mutableIgnoreAutofillIdList,
|
||||
website = website,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,12 +3,18 @@ package com.x8bit.bitwarden.data.autofill.util
|
||||
import android.app.assist.AssistStructure
|
||||
import android.view.View
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillView
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.orNullIfBlank
|
||||
|
||||
/**
|
||||
* The class name of the android edit text field.
|
||||
*/
|
||||
private const val ANDROID_EDIT_TEXT_CLASS_NAME: String = "android.widget.EditText"
|
||||
|
||||
/**
|
||||
* The default web URI scheme.
|
||||
*/
|
||||
private const val DEFAULT_SCHEME: String = "https"
|
||||
|
||||
/**
|
||||
* The set of raw autofill hints that should be ignored.
|
||||
*/
|
||||
@@ -72,10 +78,7 @@ fun AssistStructure.ViewNode.toAutofillView(): AutofillView? =
|
||||
if (supportedHint != null || this.isInputField) {
|
||||
val autofillViewData = AutofillView.Data(
|
||||
autofillId = nonNullAutofillId,
|
||||
idPackage = idPackage,
|
||||
isFocused = isFocused,
|
||||
webDomain = webDomain,
|
||||
webScheme = webScheme,
|
||||
)
|
||||
buildAutofillView(
|
||||
autofillViewData = autofillViewData,
|
||||
@@ -164,3 +167,22 @@ fun AssistStructure.ViewNode.isUsernameField(
|
||||
inputType.isUsernameInputType ||
|
||||
idEntry?.containsAnyTerms(SUPPORTED_RAW_USERNAME_HINTS) == true ||
|
||||
hint?.containsAnyTerms(SUPPORTED_RAW_USERNAME_HINTS) == true
|
||||
|
||||
/**
|
||||
* The website that this [AssistStructure.ViewNode] is a part of representing.
|
||||
*/
|
||||
val AssistStructure.ViewNode.website: String?
|
||||
get() = this
|
||||
.webDomain
|
||||
.takeUnless { it?.isBlank() == true }
|
||||
?.let { webDomain ->
|
||||
val webScheme = this
|
||||
.webScheme
|
||||
.orNullIfBlank()
|
||||
?: DEFAULT_SCHEME
|
||||
|
||||
buildUri(
|
||||
domain = webDomain,
|
||||
scheme = webScheme,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,11 +9,6 @@ import com.x8bit.bitwarden.ui.platform.base.util.orNullIfBlank
|
||||
*/
|
||||
private const val ANDROID_APP_SCHEME: String = "androidapp"
|
||||
|
||||
/**
|
||||
* The default web URI scheme.
|
||||
*/
|
||||
private const val DEFAULT_SCHEME: String = "https"
|
||||
|
||||
/**
|
||||
* Try and build a URI. The try progression looks like this:
|
||||
* 1. Try searching traversal data for website URIs.
|
||||
@@ -25,13 +20,17 @@ fun List<ViewNodeTraversalData>.buildUriOrNull(
|
||||
assistStructure: AssistStructure,
|
||||
): String? {
|
||||
// Search list of [ViewNodeTraversalData] for a website URI.
|
||||
buildWebsiteUriOrNull()
|
||||
this
|
||||
.firstOrNull { it.website != null }
|
||||
?.website
|
||||
?.let { websiteUri ->
|
||||
return websiteUri
|
||||
}
|
||||
|
||||
// Search list of [ViewNodeTraversalData] for a valid package name.
|
||||
buildPackageNameOrNull()
|
||||
this
|
||||
.firstOrNull { it.idPackage != null }
|
||||
?.idPackage
|
||||
?.let { packageName ->
|
||||
return buildUri(
|
||||
domain = packageName,
|
||||
@@ -53,7 +52,7 @@ fun List<ViewNodeTraversalData>.buildUriOrNull(
|
||||
/**
|
||||
* Combine [domain] and [scheme] into a URI.
|
||||
*/
|
||||
private fun buildUri(
|
||||
fun buildUri(
|
||||
domain: String,
|
||||
scheme: String,
|
||||
): String = "$scheme://$domain"
|
||||
@@ -74,29 +73,3 @@ private fun AssistStructure.buildPackageNameOrNull(): String? = if (windowNodeCo
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
/**
|
||||
* Search each [ViewNodeTraversalData.autofillViews] list for a valid package id. If one is found
|
||||
* return it and terminate the search.
|
||||
*/
|
||||
private fun List<ViewNodeTraversalData>.buildPackageNameOrNull(): String? =
|
||||
flatMap { it.autofillViews }
|
||||
.firstOrNull { !it.data.idPackage.isNullOrEmpty() }
|
||||
?.data
|
||||
?.idPackage
|
||||
|
||||
/**
|
||||
* Search each [ViewNodeTraversalData.autofillViews] list for a valid web domain. If one is found,
|
||||
* combine it with its scheme and return it.
|
||||
*/
|
||||
private fun List<ViewNodeTraversalData>.buildWebsiteUriOrNull(): String? =
|
||||
flatMap { it.autofillViews }
|
||||
.firstOrNull { !it.data.webDomain.isNullOrEmpty() }
|
||||
?.let { autofillView ->
|
||||
val webDomain = requireNotNull(autofillView.data.webDomain)
|
||||
val webScheme = autofillView.data.webScheme.orNullIfBlank() ?: DEFAULT_SCHEME
|
||||
buildUri(
|
||||
domain = webDomain,
|
||||
scheme = webScheme,
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user