BIT-1316: Add compatibility for browser apps (#796)

This commit is contained in:
Lucas Kivi 2024-01-25 23:46:42 -06:00 committed by Álison Fernandes
parent 0818638273
commit f2d90dda55
14 changed files with 715 additions and 284 deletions

View file

@ -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?,
)
/**

View file

@ -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?,
)

View file

@ -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,
)
}

View file

@ -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,
)
}

View file

@ -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,
)
}

View file

@ -1,3 +1,278 @@
<?xml version="1.0" encoding="utf-8" ?>
<autofill-service xmlns:android="http://schemas.android.com/apk/res/android"
android:supportsInlineSuggestions="true" />
android:supportsInlineSuggestions="true">
<!--
These compatibility packages must be kept in sync with
/xml/autofill_service_configuration.xml
-->
<compatibility-package
android:name="alook.browser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="alook.browser.google"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="app.vanadium.browser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.amazon.cloud9"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.android.browser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.android.chrome"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.android.htmlviewer"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.avast.android.secure.browser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.avg.android.secure.browser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.brave.browser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.brave.browser_beta"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.brave.browser_default"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.brave.browser_dev"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.brave.browser_nightly"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.chrome.beta"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.chrome.canary"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.chrome.dev"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.cookiegames.smartcookie"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.cookiejarapps.android.smartcookieweb"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.ecosia.android"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.google.android.apps.chrome"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.google.android.apps.chrome_dev"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.google.android.captiveportallogin"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.iode.firefox"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.jamal2367.styx"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.kiwibrowser.browser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.kiwibrowser.browser.dev"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.lemurbrowser.exts"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.microsoft.emmx"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.microsoft.emmx.beta"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.microsoft.emmx.canary"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.microsoft.emmx.dev"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.mmbox.browser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.mmbox.xbrowser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.mycompany.app.soulbrowser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.naver.whale"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.neeva.app"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.opera.browser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.opera.browser.beta"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.opera.gx"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.opera.mini.native"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.opera.mini.native.beta"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.opera.touch"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.qflair.browserq"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.qwant.liberty"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.rainsee.create"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.sec.android.app.sbrowser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.sec.android.app.sbrowser.beta"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.stoutner.privacybrowser.free"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.stoutner.privacybrowser.standard"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.vivaldi.browser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.vivaldi.browser.snapshot"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.vivaldi.browser.sopranos"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.yandex.browser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.yjllq.internet"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.yjllq.kito"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.yujian.ResideMenuDemo"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.z28j.feel"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="idm.internet.download.manager"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="idm.internet.download.manager.adm.lite"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="idm.internet.download.manager.plus"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="io.github.forkmaintainers.iceraven"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="mark.via"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="mark.via.gp"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="net.dezor.browser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="net.slions.fulguris.full.download"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="net.slions.fulguris.full.download.debug"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="net.slions.fulguris.full.playstore"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="net.slions.fulguris.full.playstore.debug"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.adblockplus.browser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.adblockplus.browser.beta"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.bromite.bromite"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.bromite.chromium"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.chromium.chrome"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.codeaurora.swe.browser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.cromite.cromite"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.gnu.icecat"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.mozilla.fenix"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.mozilla.fenix.nightly"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.mozilla.fennec_aurora"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.mozilla.fennec_fdroid"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.mozilla.firefox"
android:maxLongVersionCode="2015836711" />
<compatibility-package
android:name="org.mozilla.firefox_beta"
android:maxLongVersionCode="2015849447" />
<compatibility-package
android:name="org.mozilla.reference.browser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.mozilla.rocket"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.torproject.torbrowser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.torproject.torbrowser_alpha"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.ungoogled.chromium.extensions.stable"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.ungoogled.chromium.stable"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="us.spotco.fennec_dos"
android:maxLongVersionCode="10000000000" />
</autofill-service>

View file

@ -1,2 +1,277 @@
<?xml version="1.0" encoding="utf-8" ?>
<autofill-service />
<autofill-service xmlns:android="http://schemas.android.com/apk/res/android">
<!--
These compatibility packages must be kept in sync with
/xml-v30/autofill_service_configuration.xml
-->
<compatibility-package
android:name="alook.browser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="alook.browser.google"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="app.vanadium.browser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.amazon.cloud9"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.android.browser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.android.chrome"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.android.htmlviewer"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.avast.android.secure.browser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.avg.android.secure.browser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.brave.browser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.brave.browser_beta"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.brave.browser_default"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.brave.browser_dev"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.brave.browser_nightly"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.chrome.beta"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.chrome.canary"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.chrome.dev"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.cookiegames.smartcookie"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.cookiejarapps.android.smartcookieweb"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.ecosia.android"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.google.android.apps.chrome"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.google.android.apps.chrome_dev"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.google.android.captiveportallogin"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.iode.firefox"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.jamal2367.styx"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.kiwibrowser.browser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.kiwibrowser.browser.dev"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.lemurbrowser.exts"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.microsoft.emmx"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.microsoft.emmx.beta"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.microsoft.emmx.canary"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.microsoft.emmx.dev"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.mmbox.browser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.mmbox.xbrowser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.mycompany.app.soulbrowser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.naver.whale"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.neeva.app"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.opera.browser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.opera.browser.beta"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.opera.gx"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.opera.mini.native"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.opera.mini.native.beta"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.opera.touch"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.qflair.browserq"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.qwant.liberty"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.rainsee.create"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.sec.android.app.sbrowser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.sec.android.app.sbrowser.beta"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.stoutner.privacybrowser.free"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.stoutner.privacybrowser.standard"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.vivaldi.browser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.vivaldi.browser.snapshot"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.vivaldi.browser.sopranos"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.yandex.browser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.yjllq.internet"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.yjllq.kito"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.yujian.ResideMenuDemo"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="com.z28j.feel"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="idm.internet.download.manager"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="idm.internet.download.manager.adm.lite"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="idm.internet.download.manager.plus"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="io.github.forkmaintainers.iceraven"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="mark.via"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="mark.via.gp"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="net.dezor.browser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="net.slions.fulguris.full.download"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="net.slions.fulguris.full.download.debug"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="net.slions.fulguris.full.playstore"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="net.slions.fulguris.full.playstore.debug"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.adblockplus.browser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.adblockplus.browser.beta"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.bromite.bromite"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.bromite.chromium"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.chromium.chrome"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.codeaurora.swe.browser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.cromite.cromite"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.gnu.icecat"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.mozilla.fenix"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.mozilla.fenix.nightly"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.mozilla.fennec_aurora"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.mozilla.fennec_fdroid"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.mozilla.firefox"
android:maxLongVersionCode="2015836711" />
<compatibility-package
android:name="org.mozilla.firefox_beta"
android:maxLongVersionCode="2015849447" />
<compatibility-package
android:name="org.mozilla.reference.browser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.mozilla.rocket"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.torproject.torbrowser"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.torproject.torbrowser_alpha"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.ungoogled.chromium.extensions.stable"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="org.ungoogled.chromium.stable"
android:maxLongVersionCode="10000000000" />
<compatibility-package
android:name="us.spotco.fennec_dos"
android:maxLongVersionCode="10000000000" />
</autofill-service>

View file

@ -111,10 +111,7 @@ class FillResponseBuilderTest {
AutofillView.Login.Username(
data = AutofillView.Data(
autofillId = mockk(),
idPackage = null,
isFocused = true,
webDomain = null,
webScheme = null,
),
),
),

View file

@ -35,10 +35,7 @@ class FilledDataBuilderTest {
private val autofillId: AutofillId = mockk()
private val autofillViewData = AutofillView.Data(
autofillId = autofillId,
idPackage = null,
isFocused = false,
webDomain = null,
webScheme = null,
)
@BeforeEach

View file

@ -15,6 +15,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
import io.mockk.every
import io.mockk.mockk
@ -30,13 +31,6 @@ class AutofillParserTests {
private lateinit var parser: AutofillParser
private val autofillAppInfo: AutofillAppInfo = mockk()
private val autofillViewData = AutofillView.Data(
autofillId = mockk(),
isFocused = true,
idPackage = null,
webDomain = null,
webScheme = null,
)
private val assistStructure: AssistStructure = mockk()
private val cardAutofillHint = View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR
private val cardAutofillId: AutofillId = mockk()
@ -44,6 +38,7 @@ class AutofillParserTests {
every { this@mockk.autofillHints } returns arrayOf(cardAutofillHint)
every { this@mockk.autofillId } returns cardAutofillId
every { this@mockk.childCount } returns 0
every { this@mockk.idPackage } returns ID_PACKAGE
}
private val loginAutofillHint = View.AUTOFILL_HINT_USERNAME
private val loginAutofillId: AutofillId = mockk()
@ -51,6 +46,7 @@ class AutofillParserTests {
every { this@mockk.autofillHints } returns arrayOf(loginAutofillHint)
every { this@mockk.autofillId } returns loginAutofillId
every { this@mockk.childCount } returns 0
every { this@mockk.idPackage } returns ID_PACKAGE
}
private val cardWindowNode: AssistStructure.WindowNode = mockk {
every { this@mockk.rootViewNode } returns cardViewNode
@ -74,11 +70,14 @@ class AutofillParserTests {
@BeforeEach
fun setup() {
mockkStatic(AssistStructure.ViewNode::toAutofillView)
mockkStatic(AssistStructure.ViewNode::website)
mockkStatic(
FillRequest::getMaxInlineSuggestionsCount,
FillRequest::getInlinePresentationSpecs,
)
mockkStatic(List<ViewNodeTraversalData>::buildUriOrNull)
every { cardViewNode.website } returns WEBSITE
every { loginViewNode.website } returns WEBSITE
every {
fillRequest.getInlinePresentationSpecs(
autofillAppInfo = autofillAppInfo,
@ -112,6 +111,7 @@ class AutofillParserTests {
@AfterEach
fun teardown() {
unmockkStatic(AssistStructure.ViewNode::toAutofillView)
unmockkStatic(AssistStructure.ViewNode::website)
unmockkStatic(
FillRequest::getMaxInlineSuggestionsCount,
FillRequest::getInlinePresentationSpecs,
@ -160,13 +160,15 @@ class AutofillParserTests {
every { this@mockk.autofillHints } returns arrayOf(childAutofillHint)
every { this@mockk.autofillId } returns childAutofillId
every { this@mockk.childCount } returns 0
every { this@mockk.idPackage } returns null
every { this@mockk.isFocused } returns false
every { this@mockk.toAutofillView() } returns null
every { this@mockk.website } returns null
}
val parentAutofillHint = View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR
val parentAutofillId: AutofillId = mockk()
val parentAutofillView: AutofillView.Card = AutofillView.Card.ExpirationMonth(
data = autofillViewData.copy(
data = AutofillView.Data(
autofillId = parentAutofillId,
isFocused = true,
),
@ -174,9 +176,11 @@ class AutofillParserTests {
val parentViewNode: AssistStructure.ViewNode = mockk {
every { this@mockk.autofillHints } returns arrayOf(parentAutofillHint)
every { this@mockk.autofillId } returns parentAutofillId
every { this@mockk.idPackage } returns null
every { this@mockk.toAutofillView() } returns parentAutofillView
every { this@mockk.childCount } returns 1
every { this@mockk.getChildAt(0) } returns childViewNode
every { this@mockk.website } returns null
}
val windowNode: AssistStructure.WindowNode = mockk {
every { this@mockk.rootViewNode } returns parentViewNode
@ -220,13 +224,13 @@ class AutofillParserTests {
// Setup
setupAssistStructureWithAllAutofillViewTypes()
val cardAutofillView: AutofillView.Card = AutofillView.Card.ExpirationMonth(
data = autofillViewData.copy(
data = AutofillView.Data(
autofillId = cardAutofillId,
isFocused = true,
),
)
val loginAutofillView: AutofillView.Login = AutofillView.Login.Username(
data = autofillViewData.copy(
data = AutofillView.Data(
autofillId = loginAutofillId,
isFocused = false,
),
@ -270,13 +274,13 @@ class AutofillParserTests {
// Setup
setupAssistStructureWithAllAutofillViewTypes()
val cardAutofillView: AutofillView.Card = AutofillView.Card.ExpirationMonth(
data = autofillViewData.copy(
data = AutofillView.Data(
autofillId = cardAutofillId,
isFocused = false,
),
)
val loginAutofillView: AutofillView.Login = AutofillView.Login.Username(
data = autofillViewData.copy(
data = AutofillView.Data(
autofillId = loginAutofillId,
isFocused = true,
),
@ -320,13 +324,13 @@ class AutofillParserTests {
// Setup
setupAssistStructureWithAllAutofillViewTypes()
val cardAutofillView: AutofillView.Card = AutofillView.Card.ExpirationMonth(
data = autofillViewData.copy(
data = AutofillView.Data(
autofillId = cardAutofillId,
isFocused = true,
),
)
val loginAutofillView: AutofillView.Login = AutofillView.Login.Username(
data = autofillViewData.copy(
data = AutofillView.Data(
autofillId = loginAutofillId,
isFocused = true,
),
@ -371,13 +375,13 @@ class AutofillParserTests {
mockIsInlineAutofillEnabled = false
setupAssistStructureWithAllAutofillViewTypes()
val cardAutofillView: AutofillView.Card = AutofillView.Card.ExpirationMonth(
data = autofillViewData.copy(
data = AutofillView.Data(
autofillId = cardAutofillId,
isFocused = true,
),
)
val loginAutofillView: AutofillView.Login = AutofillView.Login.Username(
data = autofillViewData.copy(
data = AutofillView.Data(
autofillId = loginAutofillId,
isFocused = true,
),
@ -427,5 +431,7 @@ class AutofillParserTests {
}
}
private const val ID_PACKAGE: String = "com.x8bit.bitwarden"
private const val MAX_INLINE_SUGGESTION_COUNT: Int = 42
private const val URI: String = "androidapp://com.x8bit.bitwarden"
private const val WEBSITE: String = "https://www.google.com"

View file

@ -19,10 +19,7 @@ class AutofillViewExtensionsTest {
private val autofillValue: AutofillValue = mockk()
private val autofillViewData = AutofillView.Data(
autofillId = autofillId,
idPackage = null,
isFocused = false,
webDomain = null,
webScheme = null,
)
@BeforeEach

View file

@ -69,10 +69,7 @@ class FilledDataExtensionsTest {
AutofillView.Login.Username(
data = AutofillView.Data(
autofillId = autofillId,
idPackage = null,
isFocused = true,
webDomain = null,
webScheme = null,
),
),
),

View file

@ -22,20 +22,14 @@ class ViewNodeExtensionsTest {
private val expectedIsFocused = true
private val autofillViewData = AutofillView.Data(
autofillId = expectedAutofillId,
idPackage = ID_PACKAGE,
isFocused = expectedIsFocused,
webDomain = WEB_DOMAIN,
webScheme = WEB_SCHEME,
)
private val viewNode: AssistStructure.ViewNode = mockk {
every { this@mockk.autofillId } returns expectedAutofillId
every { this@mockk.childCount } returns 0
every { this@mockk.inputType } returns 1
every { this@mockk.idPackage } returns ID_PACKAGE
every { this@mockk.isFocused } returns expectedIsFocused
every { this@mockk.webDomain } returns WEB_DOMAIN
every { this@mockk.webScheme } returns WEB_SCHEME
}
@BeforeEach
@ -375,6 +369,80 @@ class ViewNodeExtensionsTest {
}
}
@Test
fun `website should return URI if domain and scheme are valid`() {
// Setup
val webDomain = "www.google.com"
val webScheme = "http"
val expected = "http://www.google.com"
every { viewNode.webDomain } returns webDomain
every { viewNode.webScheme } returns webScheme
// Test
val actual = viewNode.website
// Verify
assertEquals(expected, actual)
}
@Test
fun `website should return URI with default scheme if domain is valid and scheme is null`() {
// Setup
val webDomain = "www.google.com"
val webScheme = null
val expected = "https://www.google.com"
every { viewNode.webDomain } returns webDomain
every { viewNode.webScheme } returns webScheme
// Test
val actual = viewNode.website
// Verify
assertEquals(expected, actual)
}
@Test
fun `website should return URI with default scheme if domain is valid and scheme is blank`() {
// Setup
val webDomain = "www.google.com"
val webScheme = " "
val expected = "https://www.google.com"
every { viewNode.webDomain } returns webDomain
every { viewNode.webScheme } returns webScheme
// Test
val actual = viewNode.website
// Verify
assertEquals(expected, actual)
}
@Test
fun `website should return null when domain is null`() {
// Setup
val webDomain = null
every { viewNode.webDomain } returns webDomain
// Test
val actual = viewNode.website
// Verify
assertNull(actual)
}
@Test
fun `website should return null when domain is blank`() {
// Setup
val webDomain = " "
every { viewNode.webDomain } returns webDomain
// Test
val actual = viewNode.website
// Verify
assertNull(actual)
}
/**
* Set up [viewNode] to be an input field but not supported.
*/
@ -391,9 +459,6 @@ class ViewNodeExtensionsTest {
}
private const val ANDROID_EDIT_TEXT_CLASS_NAME: String = "android.widget.EditText"
private const val ID_PACKAGE: String = "ID_PACKAGE"
private const val WEB_DOMAIN: String = "WEB_DOMAIN"
private const val WEB_SCHEME: String = "WEB_SCHEME"
private val IGNORED_RAW_HINTS: List<String> = listOf(
"search",
"find",

View file

@ -6,7 +6,6 @@ import com.x8bit.bitwarden.data.autofill.model.ViewNodeTraversalData
import io.mockk.every
import io.mockk.mockk
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Test
class ViewNodeTraversalDataExtensionsTest {
@ -17,28 +16,18 @@ class ViewNodeTraversalDataExtensionsTest {
}
private val autofillViewData = AutofillView.Data(
autofillId = mockk(),
idPackage = null,
isFocused = false,
webDomain = null,
webScheme = null,
)
@Test
fun `buildUriOrNull should return URI when contains valid domain and scheme`() {
fun `buildUriOrNull should return website URI when present`() {
// Setup
val autofillView = AutofillView.Card.Number(
data = autofillViewData.copy(
webDomain = WEB_DOMAIN,
webScheme = WEB_SCHEME,
),
)
val viewNodeTraversalData = ViewNodeTraversalData(
autofillViews = listOf(
autofillView,
),
autofillViews = emptyList(),
idPackage = null,
ignoreAutofillIds = emptyList(),
website = WEBSITE,
)
val expected = "$WEB_SCHEME://$WEB_DOMAIN"
// Test
val actual = listOf(viewNodeTraversalData).buildUriOrNull(
@ -46,77 +35,17 @@ class ViewNodeTraversalDataExtensionsTest {
)
// Verify
assertEquals(expected, actual)
assertEquals(WEBSITE, actual)
}
@Test
fun `buildUriOrNull should return URI with default scheme when domain valid and scheme null`() {
fun `buildUriOrNull should return idPackage URI when WEBSITE is null`() {
// Setup
val autofillView = AutofillView.Card.Number(
data = autofillViewData.copy(
webDomain = WEB_DOMAIN,
webScheme = null,
),
)
val viewNodeTraversalData = ViewNodeTraversalData(
autofillViews = listOf(
autofillView,
),
ignoreAutofillIds = emptyList(),
)
val expected = "https://$WEB_DOMAIN"
// Test
val actual = listOf(viewNodeTraversalData).buildUriOrNull(
assistStructure = assistStructure,
)
// Verify
assertEquals(expected, actual)
}
@Suppress("MaxLineLength")
@Test
fun `buildUriOrNull should return URI with default scheme when domain valid and scheme empty`() {
// Setup
val autofillView = AutofillView.Card.Number(
data = autofillViewData.copy(
webDomain = WEB_DOMAIN,
webScheme = "",
),
)
val viewNodeTraversalData = ViewNodeTraversalData(
autofillViews = listOf(
autofillView,
),
ignoreAutofillIds = emptyList(),
)
val expected = "https://$WEB_DOMAIN"
// Test
val actual = listOf(viewNodeTraversalData).buildUriOrNull(
assistStructure = assistStructure,
)
// Verify
assertEquals(expected, actual)
}
@Test
fun `buildUriOrNull should return idPackage URI when domain is null`() {
// Setup
val autofillView = AutofillView.Card.Number(
data = autofillViewData.copy(
idPackage = ID_PACKAGE,
webDomain = null,
webScheme = null,
),
)
val viewNodeTraversalData = ViewNodeTraversalData(
autofillViews = listOf(
autofillView,
),
autofillViews = emptyList(),
idPackage = ID_PACKAGE,
ignoreAutofillIds = emptyList(),
website = null,
)
val expected = "androidapp://$ID_PACKAGE"
@ -130,47 +59,13 @@ class ViewNodeTraversalDataExtensionsTest {
}
@Test
fun `buildUriOrNull should return idPackage URI when domain is empty`() {
fun `buildUriOrNull should return title URI when website and idPackage are null`() {
// Setup
val autofillView = AutofillView.Card.Number(
data = autofillViewData.copy(
idPackage = ID_PACKAGE,
webDomain = "",
webScheme = "",
),
)
val viewNodeTraversalData = ViewNodeTraversalData(
autofillViews = listOf(
autofillView,
),
ignoreAutofillIds = emptyList(),
)
val expected = "androidapp://$ID_PACKAGE"
// Test
val actual = listOf(viewNodeTraversalData).buildUriOrNull(
assistStructure = assistStructure,
)
// Verify
assertEquals(expected, actual)
}
@Test
fun `buildUriOrNull should return title URI when domain and idPackage are null`() {
// Setup
val autofillView = AutofillView.Card.Number(
data = autofillViewData.copy(
idPackage = null,
webDomain = null,
webScheme = null,
),
)
val viewNodeTraversalData = ViewNodeTraversalData(
autofillViews = listOf(
autofillView,
),
autofillViews = emptyList(),
idPackage = null,
ignoreAutofillIds = emptyList(),
website = null,
)
val expected = "androidapp://com.x8bit.bitwarden"
every { windowNode.title } returns "com.x8bit.bitwarden/path.deeper.into.app"
@ -184,91 +79,8 @@ class ViewNodeTraversalDataExtensionsTest {
assertEquals(expected, actual)
}
@Test
fun `buildUriOrNull should return title URI when domain and idPackage are empty`() {
// Setup
val autofillView = AutofillView.Card.Number(
data = autofillViewData.copy(
idPackage = "",
webDomain = "",
webScheme = null,
),
)
val viewNodeTraversalData = ViewNodeTraversalData(
autofillViews = listOf(
autofillView,
),
ignoreAutofillIds = emptyList(),
)
val expected = "androidapp://com.x8bit.bitwarden"
every { windowNode.title } returns "com.x8bit.bitwarden/path.deeper.into.app"
// Test
val actual = listOf(viewNodeTraversalData).buildUriOrNull(
assistStructure = assistStructure,
)
// Verify
assertEquals(expected, actual)
}
@Test
fun `buildUriOrNull should return null when title, domain, and idPackage are null`() {
// Setup
val autofillView = AutofillView.Card.Number(
data = autofillViewData.copy(
idPackage = null,
webDomain = null,
webScheme = null,
),
)
val viewNodeTraversalData = ViewNodeTraversalData(
autofillViews = listOf(
autofillView,
),
ignoreAutofillIds = emptyList(),
)
every { windowNode.title } returns null
// Test
val actual = listOf(viewNodeTraversalData).buildUriOrNull(
assistStructure = assistStructure,
)
// Verify
assertNull(actual)
}
@Test
fun `buildUriOrNull should return null when title, domain, and idPackage are empty`() {
// Setup
val autofillView = AutofillView.Card.Number(
data = autofillViewData.copy(
idPackage = "",
webDomain = "",
webScheme = null,
),
)
val viewNodeTraversalData = ViewNodeTraversalData(
autofillViews = listOf(
autofillView,
),
ignoreAutofillIds = emptyList(),
)
every { windowNode.title } returns ""
// Test
val actual = listOf(viewNodeTraversalData).buildUriOrNull(
assistStructure = assistStructure,
)
// Verify
assertNull(actual)
}
companion object {
private const val ID_PACKAGE: String = "com.x8bit.bitwarden"
private const val WEB_DOMAIN: String = "www.google.com"
private const val WEB_SCHEME: String = "https"
private const val WEBSITE: String = "https://www.google.com"
}
}