aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.idea/gradle.xml1
-rw-r--r--CHANGELOG.md4
-rw-r--r--app/build.gradle.kts1
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillResponseBuilder.kt105
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillViewUtils.kt132
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/autofill/oreo/OreoAutofillService.kt8
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/autofill/oreo/ui/AutofillFilterActivity.kt7
-rw-r--r--app/src/main/res/layout/activity_oreo_autofill_filter.xml18
-rw-r--r--app/src/main/res/layout/oreo_autofill_dataset.xml7
-rw-r--r--app/src/main/res/values-de/strings.xml5
-rw-r--r--app/src/main/res/values-es/strings.xml2
-rw-r--r--app/src/main/res/values-fr/strings.xml6
-rw-r--r--app/src/main/res/values-pt-rBR/strings.xml6
-rw-r--r--app/src/main/res/values-ru/strings.xml6
-rw-r--r--app/src/main/res/values/strings.xml9
-rw-r--r--app/src/main/res/xml/oreo_autofill_service.xml1
16 files changed, 211 insertions, 107 deletions
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index be83284a..43336cba 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -17,6 +17,7 @@
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
+ <option name="useQualifiedModuleNames" value="true" />
</GradleProjectSettings>
</option>
</component>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 312bfb02..d318df31 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
## [Unreleased]
+### Added
+
+- On Android 11, Autofill will use the new [inline autofill](https://developer.android.com/guide/topics/text/ime-autofill#configure-provider) UI that integrates Autofill results into your keyboard app.
+
### Fixed
- Cancelling the Autofill "Generate password" action now correctly returns you to the original app.
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index e56ebd52..87e436a0 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -89,6 +89,7 @@ dependencies {
compileOnly(Dependencies.AndroidX.annotation)
implementation(Dependencies.AndroidX.activity_ktx)
implementation(Dependencies.AndroidX.appcompat)
+ implementation(Dependencies.AndroidX.autofill)
implementation(Dependencies.AndroidX.biometric)
implementation(Dependencies.AndroidX.constraint_layout)
implementation(Dependencies.AndroidX.core_ktx)
diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillResponseBuilder.kt b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillResponseBuilder.kt
index ec3d2b77..2c62f935 100644
--- a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillResponseBuilder.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillResponseBuilder.kt
@@ -12,7 +12,8 @@ import android.service.autofill.Dataset
import android.service.autofill.FillCallback
import android.service.autofill.FillResponse
import android.service.autofill.SaveInfo
-import android.widget.RemoteViews
+import android.view.inputmethod.InlineSuggestionsRequest
+import android.widget.inline.InlinePresentationSpec
import androidx.annotation.RequiresApi
import com.github.ajalt.timberkt.e
import com.github.androidpasswordstore.autofillparser.AutofillAction
@@ -41,70 +42,85 @@ class AutofillResponseBuilder(form: FillableForm) {
scenario.fieldsToSave.minus(listOfNotNull(scenario.username)).isNotEmpty()
private val canBeSaved = saveFlags != null && scenarioSupportsSave
- private fun makePlaceholderDataset(
- remoteView: RemoteViews,
+ private fun makeIntentDataset(
+ context: Context,
+ action: AutofillAction,
intentSender: IntentSender,
- action: AutofillAction
+ metadata: DatasetMetadata,
+ imeSpec: InlinePresentationSpec?,
): Dataset {
- return Dataset.Builder(remoteView).run {
+ return Dataset.Builder(makeRemoteView(context, metadata)).run {
fillWith(scenario, action, credentials = null)
setAuthentication(intentSender)
+ if (imeSpec != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ val inlinePresentation = makeInlinePresentation(context, imeSpec, metadata)
+ if (inlinePresentation != null) {
+ setInlinePresentation(inlinePresentation)
+ }
+ }
build()
}
}
- private fun makeMatchDataset(context: Context, file: File): Dataset? {
+ private fun makeMatchDataset(context: Context, file: File, imeSpec: InlinePresentationSpec?): Dataset? {
if (scenario.fieldsToFillOn(AutofillAction.Match).isEmpty()) return null
- val remoteView = makeFillMatchRemoteView(context, file, formOrigin)
+ val metadata = makeFillMatchMetadata(context, file)
val intentSender = AutofillDecryptActivity.makeDecryptFileIntentSender(file, context)
- return makePlaceholderDataset(remoteView, intentSender, AutofillAction.Match)
+ return makeIntentDataset(context, AutofillAction.Match, intentSender, metadata, imeSpec)
}
- private fun makeSearchDataset(context: Context): Dataset? {
+ private fun makeSearchDataset(context: Context, imeSpec: InlinePresentationSpec?): Dataset? {
if (scenario.fieldsToFillOn(AutofillAction.Search).isEmpty()) return null
- val remoteView = makeSearchAndFillRemoteView(context, formOrigin)
+ val metadata = makeSearchAndFillMetadata(context)
val intentSender =
AutofillFilterView.makeMatchAndDecryptFileIntentSender(context, formOrigin)
- return makePlaceholderDataset(remoteView, intentSender, AutofillAction.Search)
+ return makeIntentDataset(context, AutofillAction.Search, intentSender, metadata, imeSpec)
}
- private fun makeGenerateDataset(context: Context): Dataset? {
+ private fun makeGenerateDataset(context: Context, imeSpec: InlinePresentationSpec?): Dataset? {
if (scenario.fieldsToFillOn(AutofillAction.Generate).isEmpty()) return null
- val remoteView = makeGenerateAndFillRemoteView(context, formOrigin)
+ val metadata = makeGenerateAndFillMetadata(context)
val intentSender = AutofillSaveActivity.makeSaveIntentSender(context, null, formOrigin)
- return makePlaceholderDataset(remoteView, intentSender, AutofillAction.Generate)
+ return makeIntentDataset(context, AutofillAction.Generate, intentSender, metadata, imeSpec)
}
- private fun makeFillOtpFromSmsDataset(context: Context): Dataset? {
+ private fun makeFillOtpFromSmsDataset(context: Context, imeSpec: InlinePresentationSpec?): Dataset? {
if (scenario.fieldsToFillOn(AutofillAction.FillOtpFromSms).isEmpty()) return null
if (!AutofillSmsActivity.shouldOfferFillFromSms(context)) return null
- val remoteView = makeFillOtpFromSmsRemoteView(context, formOrigin)
+ val metadata = makeFillOtpFromSmsMetadata(context)
val intentSender = AutofillSmsActivity.makeFillOtpFromSmsIntentSender(context)
- return makePlaceholderDataset(remoteView, intentSender, AutofillAction.FillOtpFromSms)
+ return makeIntentDataset(context, AutofillAction.FillOtpFromSms, intentSender, metadata, imeSpec)
}
private fun makePublisherChangedDataset(
context: Context,
- publisherChangedException: AutofillPublisherChangedException
+ publisherChangedException: AutofillPublisherChangedException,
+ imeSpec: InlinePresentationSpec?
): Dataset {
- val remoteView = makeWarningRemoteView(context)
+ val metadata = makeWarningMetadata(context)
// If the user decides to trust the new publisher, they can choose reset the list of
// matches. In this case we need to immediately show a new `FillResponse` as if the app were
// autofilled for the first time. This `FillResponse` needs to be returned as a result from
// `AutofillPublisherChangedActivity`, which is why we create and pass it on here.
- val fillResponseAfterReset = makeFillResponse(context, emptyList())
+ val fillResponseAfterReset = makeFillResponse(context, null, emptyList())
val intentSender = AutofillPublisherChangedActivity.makePublisherChangedIntentSender(
context, publisherChangedException, fillResponseAfterReset
)
- return makePlaceholderDataset(remoteView, intentSender, AutofillAction.Match)
+ return makeIntentDataset(context, AutofillAction.Match, intentSender, metadata, imeSpec)
}
private fun makePublisherChangedResponse(
context: Context,
+ inlineSuggestionsRequest: InlineSuggestionsRequest?,
publisherChangedException: AutofillPublisherChangedException
): FillResponse {
+ val imeSpec = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ inlineSuggestionsRequest?.inlinePresentationSpecs?.firstOrNull()
+ } else {
+ null
+ }
return FillResponse.Builder().run {
- addDataset(makePublisherChangedDataset(context, publisherChangedException))
+ addDataset(makePublisherChangedDataset(context, publisherChangedException, imeSpec))
setIgnoredIds(*ignoredIds.toTypedArray())
build()
}
@@ -127,28 +143,36 @@ class AutofillResponseBuilder(form: FillableForm) {
}
}
- private fun makeFillResponse(context: Context, matchedFiles: List<File>): FillResponse? {
- var hasDataset = false
+ private fun makeFillResponse(context: Context, inlineSuggestionsRequest: InlineSuggestionsRequest?, matchedFiles: List<File>): FillResponse? {
+ var datasetCount = 0
+ val imeSpecs: List<InlinePresentationSpec> = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ inlineSuggestionsRequest?.inlinePresentationSpecs
+ } else {
+ null
+ } ?: emptyList()
return FillResponse.Builder().run {
for (file in matchedFiles) {
- makeMatchDataset(context, file)?.let {
- hasDataset = true
+ makeMatchDataset(context, file, imeSpecs.getOrNull(datasetCount))?.let {
+ datasetCount++
addDataset(it)
}
}
- makeSearchDataset(context)?.let {
- hasDataset = true
+ makeSearchDataset(context, imeSpecs.getOrNull(datasetCount))?.let {
+ datasetCount++
addDataset(it)
}
- makeGenerateDataset(context)?.let {
- hasDataset = true
+ makeGenerateDataset(context, imeSpecs.getOrNull(datasetCount))?.let {
+ datasetCount++
addDataset(it)
}
- makeFillOtpFromSmsDataset(context)?.let {
- hasDataset = true
+ makeFillOtpFromSmsDataset(context, imeSpecs.getOrNull(datasetCount))?.let {
+ datasetCount++
addDataset(it)
}
- if (!hasDataset) return null
+ if (datasetCount == 0) return null
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ setHeader(makeRemoteView(context, makeHeaderMetadata(formOrigin.getPrettyIdentifier(context, untrusted = true))))
+ }
makeSaveInfo()?.let { setSaveInfo(it) }
setClientState(clientState)
setIgnoredIds(*ignoredIds.toTypedArray())
@@ -159,14 +183,14 @@ class AutofillResponseBuilder(form: FillableForm) {
/**
* Creates and returns a suitable [FillResponse] to the Autofill framework.
*/
- fun fillCredentials(context: Context, callback: FillCallback) {
+ fun fillCredentials(context: Context, inlineSuggestionsRequest: InlineSuggestionsRequest?, callback: FillCallback) {
AutofillMatcher.getMatchesFor(context, formOrigin).fold(
success = { matchedFiles ->
- callback.onSuccess(makeFillResponse(context, matchedFiles))
+ callback.onSuccess(makeFillResponse(context, inlineSuggestionsRequest, matchedFiles))
},
failure = { e ->
e(e)
- callback.onSuccess(makePublisherChangedResponse(context, e))
+ callback.onSuccess(makePublisherChangedResponse(context, inlineSuggestionsRequest, e))
}
)
}
@@ -178,14 +202,17 @@ class AutofillResponseBuilder(form: FillableForm) {
clientState: Bundle,
action: AutofillAction
): Dataset {
- val remoteView = makePlaceholderRemoteView(context)
val scenario = AutofillScenario.fromBundle(clientState)
// Before Android P, Datasets used for fill-in had to come with a RemoteViews, even
- // though they are never shown.
+ // though they are rarely shown.
+ // FIXME: We should clone the original dataset here and add the credentials to be filled
+ // in. Otherwise, the entry in the cached list of datasets will be overwritten by the
+ // fill-in dataset without any visual representation. This causes it to be missing from
+ // the Autofill suggestions shown after the user clears the filled out form fields.
val builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
Dataset.Builder()
} else {
- Dataset.Builder(remoteView)
+ Dataset.Builder(makeRemoteView(context, makeEmptyMetadata()))
}
return builder.run {
if (scenario != null) fillWith(scenario, action, credentials)
diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillViewUtils.kt b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillViewUtils.kt
index 5e8061a2..49e0d3e3 100644
--- a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillViewUtils.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillViewUtils.kt
@@ -4,64 +4,110 @@
*/
package com.zeapo.pwdstore.autofill.oreo
+import android.annotation.SuppressLint
+import android.app.PendingIntent
import android.content.Context
+import android.content.Intent
+import android.content.res.Resources
+import android.graphics.drawable.Icon
+import android.os.Build
+import android.service.autofill.InlinePresentation
+import android.view.View
import android.widget.RemoteViews
-import com.github.androidpasswordstore.autofillparser.FormOrigin
+import android.widget.inline.InlinePresentationSpec
+import androidx.annotation.DrawableRes
+import androidx.autofill.inline.UiVersions
+import androidx.autofill.inline.v1.InlineSuggestionUi
+import com.zeapo.pwdstore.PasswordStore
import com.zeapo.pwdstore.R
import com.zeapo.pwdstore.utils.PasswordRepository
import java.io.File
-private fun makeRemoteView(
- context: Context,
- title: String,
- summary: String,
- iconRes: Int
-): RemoteViews {
+data class DatasetMetadata(val title: String, val subtitle: String?, @DrawableRes val iconRes: Int)
+
+fun makeRemoteView(context: Context, metadata: DatasetMetadata): RemoteViews {
return RemoteViews(context.packageName, R.layout.oreo_autofill_dataset).apply {
- setTextViewText(R.id.title, title)
- setTextViewText(R.id.summary, summary)
- setImageViewResource(R.id.icon, iconRes)
+ setTextViewText(R.id.title, metadata.title)
+ if (metadata.subtitle != null) {
+ setTextViewText(R.id.summary, metadata.subtitle)
+ } else {
+ setViewVisibility(R.id.summary, View.GONE)
+ }
+ if (metadata.iconRes != Resources.ID_NULL) {
+ setImageViewResource(R.id.icon, metadata.iconRes)
+ } else {
+ setViewVisibility(R.id.icon, View.GONE)
+ }
}
}
-fun makeFillMatchRemoteView(context: Context, file: File, formOrigin: FormOrigin): RemoteViews {
- val title = formOrigin.getPrettyIdentifier(context, untrusted = false)
+@SuppressLint("RestrictedApi")
+fun makeInlinePresentation(context: Context, imeSpec: InlinePresentationSpec, metadata: DatasetMetadata): InlinePresentation? {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R)
+ return null
+
+ if (UiVersions.INLINE_UI_VERSION_1 !in UiVersions.getVersions(imeSpec.style))
+ return null
+
+ val launchIntent = PendingIntent.getActivity(context, 0, Intent(context, PasswordStore::class.java), 0)
+ val slice = InlineSuggestionUi.newContentBuilder(launchIntent).run {
+ setTitle(metadata.title)
+ if (metadata.subtitle != null)
+ setSubtitle(metadata.subtitle)
+ setContentDescription(if (metadata.subtitle != null) "${metadata.title} - ${metadata.subtitle}" else metadata.title)
+ setStartIcon(Icon.createWithResource(context, metadata.iconRes))
+ build().slice
+ }
+
+ return InlinePresentation(slice, imeSpec, false)
+}
+
+
+fun makeFillMatchMetadata(context: Context, file: File): DatasetMetadata {
val directoryStructure = AutofillPreferences.directoryStructure(context)
val relativeFile = file.relativeTo(PasswordRepository.getRepositoryDirectory())
- val summary = directoryStructure.getUsernameFor(relativeFile)
- ?: directoryStructure.getPathToIdentifierFor(relativeFile) ?: ""
- val iconRes = R.drawable.ic_person_black_24dp
- return makeRemoteView(context, title, summary, iconRes)
+ val title = directoryStructure.getIdentifierFor(relativeFile)
+ ?: directoryStructure.getAccountPartFor(relativeFile)!!
+ val subtitle = directoryStructure.getAccountPartFor(relativeFile)
+ return DatasetMetadata(
+ title,
+ subtitle,
+ R.drawable.ic_person_black_24dp
+ )
}
-fun makeSearchAndFillRemoteView(context: Context, formOrigin: FormOrigin): RemoteViews {
- val title = formOrigin.getPrettyIdentifier(context, untrusted = true)
- val summary = context.getString(R.string.oreo_autofill_search_in_store)
- val iconRes = R.drawable.ic_search_black_24dp
- return makeRemoteView(context, title, summary, iconRes)
-}
+fun makeSearchAndFillMetadata(context: Context) = DatasetMetadata(
+ context.getString(R.string.oreo_autofill_search_in_store),
+ null,
+ R.drawable.ic_search_black_24dp
+)
-fun makeGenerateAndFillRemoteView(context: Context, formOrigin: FormOrigin): RemoteViews {
- val title = formOrigin.getPrettyIdentifier(context, untrusted = true)
- val summary = context.getString(R.string.oreo_autofill_generate_password)
- val iconRes = R.drawable.ic_autofill_new_password
- return makeRemoteView(context, title, summary, iconRes)
-}
+fun makeGenerateAndFillMetadata(context: Context) = DatasetMetadata(
+ context.getString(R.string.oreo_autofill_generate_password),
+ null,
+ R.drawable.ic_autofill_new_password
+)
-fun makeFillOtpFromSmsRemoteView(context: Context, formOrigin: FormOrigin): RemoteViews {
- val title = formOrigin.getPrettyIdentifier(context, untrusted = true)
- val summary = context.getString(R.string.oreo_autofill_fill_otp_from_sms)
- val iconRes = R.drawable.ic_autofill_sms
- return makeRemoteView(context, title, summary, iconRes)
-}
+fun makeFillOtpFromSmsMetadata(context: Context) = DatasetMetadata(
+ context.getString(R.string.oreo_autofill_fill_otp_from_sms),
+ null,
+ R.drawable.ic_autofill_sms
+)
-fun makePlaceholderRemoteView(context: Context): RemoteViews {
- return makeRemoteView(context, "PLACEHOLDER", "PLACEHOLDER", R.mipmap.ic_launcher)
-}
+fun makeEmptyMetadata() = DatasetMetadata(
+ "PLACEHOLDER",
+ "PLACEHOLDER",
+ R.mipmap.ic_launcher
+)
-fun makeWarningRemoteView(context: Context): RemoteViews {
- val title = context.getString(R.string.oreo_autofill_warning_publisher_dataset_title)
- val summary = context.getString(R.string.oreo_autofill_warning_publisher_dataset_summary)
- val iconRes = R.drawable.ic_warning_red_24dp
- return makeRemoteView(context, title, summary, iconRes)
-}
+fun makeWarningMetadata(context: Context) = DatasetMetadata(
+ context.getString(R.string.oreo_autofill_warning_publisher_dataset_title),
+ context.getString(R.string.oreo_autofill_warning_publisher_dataset_summary),
+ R.drawable.ic_warning_red_24dp
+)
+
+fun makeHeaderMetadata(title: String) = DatasetMetadata(
+ title,
+ null,
+ 0
+)
diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/OreoAutofillService.kt b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/OreoAutofillService.kt
index ecec6747..10831cc5 100644
--- a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/OreoAutofillService.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/OreoAutofillService.kt
@@ -86,7 +86,13 @@ class OreoAutofillService : AutofillService() {
callback.onSuccess(null)
return
}
- AutofillResponseBuilder(formToFill).fillCredentials(this, callback)
+ val inlineSuggestionsRequest =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ request.inlineSuggestionsRequest
+ } else {
+ null
+ }
+ AutofillResponseBuilder(formToFill).fillCredentials(this, inlineSuggestionsRequest, callback)
}
override fun onSaveRequest(request: SaveRequest, callback: SaveCallback) {
diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/ui/AutofillFilterActivity.kt b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/ui/AutofillFilterActivity.kt
index f22c6596..25919ad5 100644
--- a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/ui/AutofillFilterActivity.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/ui/AutofillFilterActivity.kt
@@ -162,6 +162,13 @@ class AutofillFilterView : AppCompatActivity() {
setText(initialSearch, TextView.BufferType.EDITABLE)
addTextChangedListener { updateSearch() }
}
+ origin.text = buildSpannedString {
+ append(getString(R.string.oreo_autofill_select_and_fill_into))
+ append("\n")
+ bold {
+ append(formOrigin.getPrettyIdentifier(applicationContext, untrusted = true))
+ }
+ }
strictDomainSearch.apply {
visibility = if (formOrigin is FormOrigin.Web) View.VISIBLE else View.GONE
isChecked = formOrigin is FormOrigin.Web
diff --git a/app/src/main/res/layout/activity_oreo_autofill_filter.xml b/app/src/main/res/layout/activity_oreo_autofill_filter.xml
index a3718b43..8e671e30 100644
--- a/app/src/main/res/layout/activity_oreo_autofill_filter.xml
+++ b/app/src/main/res/layout/activity_oreo_autofill_filter.xml
@@ -14,16 +14,28 @@
<ImageView
android:id="@+id/cover"
android:layout_width="0dp"
- android:layout_height="80dp"
+ android:layout_height="60dp"
android:background="@color/primary_color"
android:contentDescription="@string/app_name"
android:src="@mipmap/ic_launcher_foreground"
- app:layout_constraintBottom_toTopOf="@id/searchLayout"
+ app:layout_constraintBottom_toTopOf="@id/origin"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" />
+ <TextView
+ android:id="@+id/origin"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="false"
+ android:textAlignment="center"
+ app:layout_constraintBottom_toTopOf="@id/searchLayout"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/cover"
+ app:layout_constraintVertical_bias="0.0" />
+
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/searchLayout"
android:layout_width="match_parent"
@@ -35,7 +47,7 @@
app:layout_constraintBottom_toTopOf="@id/rvPasswordSwitcher"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/cover">
+ app:layout_constraintTop_toBottomOf="@id/origin">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/search"
diff --git a/app/src/main/res/layout/oreo_autofill_dataset.xml b/app/src/main/res/layout/oreo_autofill_dataset.xml
index 2e9f48b9..e13d1688 100644
--- a/app/src/main/res/layout/oreo_autofill_dataset.xml
+++ b/app/src/main/res/layout/oreo_autofill_dataset.xml
@@ -6,9 +6,10 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:orientation="horizontal"
+ android:gravity="center_vertical"
android:paddingLeft="10dp"
android:paddingTop="5dp"
android:paddingRight="10dp"
@@ -20,6 +21,8 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginEnd="10dp"
+ android:layout_marginTop="5dp"
+ android:layout_marginBottom="5dp"
android:adjustViewBounds="true"
android:maxWidth="20dp"
android:maxHeight="20dp"
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index add23eda..82e75d68 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -157,12 +157,15 @@
<string name="send_plaintext_password_to">Passwort unverschlüsselt senden an…</string>
<string name="app_icon_hint">App Icon</string>
<!-- Oreo Autofill -->
+ <string name="oreo_autofill_select_and_fill_into">Eintrag auswählen für Autofill in</string>
<string name="oreo_autofill_strict_domain_search">Phishing-resistente Suche</string>
<string name="oreo_autofill_filter_no_results">Keine Ergebnisse.</string>
<string name="oreo_autofill_save_internal_error">Speichern aufgrund eines internen Fehlers fehlgeschlagen</string>
<string name="oreo_autofill_save_app_not_supported">Diese App wird derzeit nicht unterstützt</string>
<string name="oreo_autofill_save_passwords_dont_match">Die Passwörter stimmen nicht überein</string>
- <string name="oreo_autofill_generate_password">Passwort generieren…</string>
+ <string name="oreo_autofill_generate_password">Eintrag erstellen</string>
+ <string name="oreo_autofill_search_in_store">Eintrag suchen</string>
+ <string name="oreo_autofill_fill_otp_from_sms">Code aus SMS einfügen</string>
<string name="oreo_autofill_warning_publisher_footer"><b>Die derzeit installierte App versucht, Ihre Anmeldeinformationen zu stehlen, indem sie vorgibt, eine vertrauenswürdige App zu sein.</b>\n\nVersuchen Sie die App zu deinstallieren und installieren Sie sie erneut aus einer vertrauenswürdigen Quelle wie dem Play Store, Amazon Appstore, F-Droid oder dem Shop Ihres Telefonherstellers.</string>
<string name="oreo_autofill_warning_publisher_install_time">Installiert: %1$s</string>
<string name="oreo_autofill_warning_publisher_warning_sign_description">Warnung</string>
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index c64287b5..f19b6817 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -130,11 +130,9 @@
<!-- Oreo Autofill -->
<string name="oreo_autofill_match_with">Coincide con %1$s</string>
<string name="oreo_autofill_filter_no_results">Sin resultados.</string>
- <string name="oreo_autofill_search_in_store">Buscar en la tienda…</string>
<string name="oreo_autofill_save_internal_error">Error al guardar debido a un error interno</string>
<string name="oreo_autofill_save_app_not_supported">Esta aplicación no es compatible actualmente</string>
<string name="oreo_autofill_save_passwords_dont_match">Las contraseñas no coinciden</string>
- <string name="oreo_autofill_generate_password">Generar contraseña…</string>
<string name="oreo_autofill_warning_publisher_install_time">Instalada: %1$s</string>
<string name="oreo_autofill_warning_publisher_advanced_info_button">Información avanzada</string>
<string name="oreo_autofill_warning_publisher_changed_disable_autofill_button">Mantener el Autocompletado desactivado</string>
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 557591e8..10fa1130 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -185,17 +185,15 @@
<string name="oreo_autofill_match_with">Apparier avec %1$s</string>
<string name="oreo_autofill_matches_clear_existing">Effacer l’appairage actuel</string>
<string name="oreo_autofill_filter_no_results">Aucun résultat.</string>
- <string name="oreo_autofill_search_in_store">Rechercher dans le dépôt…</string>
<string name="oreo_autofill_save_internal_error">Échec de la sauvegarde : erreur interne</string>
<string name="oreo_autofill_save_app_not_supported">Cette application n\'est actuellement pas prise en charge</string>
<string name="oreo_autofill_save_passwords_dont_match">Les mots de passe ne coïncident pas</string>
- <string name="oreo_autofill_generate_password">Générer un mot de passe…</string>
- <string name="oreo_autofill_fill_otp_from_sms">Extraire le code depuis un SMS…</string>
+ <string name="oreo_autofill_fill_otp_from_sms">Extraire le code depuis un SMS</string>
<string name="oreo_autofill_warning_publisher_header">L\'éditeur de cette application a changé depuis que vous avez appairé un mot de passe avec celle-ci:</string>
<string name="oreo_autofill_warning_publisher_footer"><b>L\'application actuellement installée peut essayer de voler vos identifiants en faisant semblant d\'être une application de confiance.</b>\n\nEssayez de désinstaller et de réinstaller l\'application à partir d\'une source fiable, comme le Play Store, l\'AppStore d\'Amazon, le F-Droid ou la boutique du fabricant de votre téléphone.</string>
<string name="oreo_autofill_warning_publisher_install_time">Installé : %1$s</string>
<string name="oreo_autofill_warning_publisher_warning_sign_description">Avertissement</string>
- <string name="oreo_autofill_warning_publisher_dataset_summary">Appuyez pour en savoir plus…</string>
+ <string name="oreo_autofill_warning_publisher_dataset_summary">Appuyez pour en savoir plus</string>
<string name="oreo_autofill_warning_publisher_dataset_title">Tentative possible d\'hameçonnage</string>
<string name="oreo_autofill_general_fill_and_save_support">Remplir et enregistrer les identifiants</string>
<string name="oreo_autofill_general_fill_support">Remplir les identifiants</string>
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index cef14227..c45b2705 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -187,12 +187,10 @@
<string name="oreo_autofill_match_with">Combinar com %1$s</string>
<string name="oreo_autofill_matches_clear_existing">Limpar correspondências existentes</string>
<string name="oreo_autofill_filter_no_results">Sem resultados.</string>
- <string name="oreo_autofill_search_in_store">Pesquisar no armazenamento…</string>
<string name="oreo_autofill_save_internal_error">Falha ao salvar devido a um erro interno</string>
<string name="oreo_autofill_save_app_not_supported">Este app não é suportado no momento</string>
<string name="oreo_autofill_save_passwords_dont_match">As senhas não coincidem</string>
- <string name="oreo_autofill_generate_password">Gerar senha…</string>
- <string name="oreo_autofill_fill_otp_from_sms">Extrair código do SMS…</string>
+ <string name="oreo_autofill_fill_otp_from_sms">Extrair código do SMS</string>
<string name="oreo_autofill_max_matches_reached">Número máximo de correspondências (%1$d) atingidas; limpar correspondências antes de adicionar novas.</string>
<string name="oreo_autofill_warning_publisher_header">O editor deste aplicativo mudou desde a primeira vez que você associou uma entrada no Password Store:</string>
<string name="oreo_autofill_warning_publisher_footer"><b>O aplicativo atualmente instalado pode estar tentando roubar suas credenciais fingindo ser um aplicativo confiável.</b>\n\nTente desinstalar e reinstalar o aplicativo de uma fonte confiável, como a Play Store, Amazon Appstore, F-Droid ou a loja do fabricante do seu telefone.</string>
@@ -201,7 +199,7 @@
<string name="oreo_autofill_warning_publisher_changed_disable_autofill_button">Manter o preenchimento automático desativado</string>
<string name="oreo_autofill_warning_publisher_reenable_button">Reativar preenchimento automático</string>
<string name="oreo_autofill_warning_publisher_warning_sign_description">Alerta</string>
- <string name="oreo_autofill_warning_publisher_dataset_summary">Toque para detalhes…</string>
+ <string name="oreo_autofill_warning_publisher_dataset_summary">Toque para detalhes</string>
<string name="oreo_autofill_warning_publisher_dataset_title">Possível tentativa de phishing</string>
<string name="oreo_autofill_general_fill_and_save_support">Preencher e salvar credenciais</string>
<string name="oreo_autofill_general_fill_support">Preencher as credenciais</string>
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index f9340b41..baa49c74 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -191,12 +191,10 @@
<string name="oreo_autofill_match_with">Совпадает с %1$s</string>
<string name="oreo_autofill_matches_clear_existing">Очистить существующие совпадения</string>
<string name="oreo_autofill_filter_no_results">Не найдено.</string>
- <string name="oreo_autofill_search_in_store">Искать в хранилище...</string>
<string name="oreo_autofill_save_internal_error">Сохранение не удалось из-за внутренней ошибки</string>
<string name="oreo_autofill_save_app_not_supported">Это приложение в настоящее время не поддерживается</string>
<string name="oreo_autofill_save_passwords_dont_match">Пароли не совпадают</string>
- <string name="oreo_autofill_generate_password">Сгенерировать пароль...</string>
- <string name="oreo_autofill_fill_otp_from_sms">Извлечение кодов из SMS…</string>
+ <string name="oreo_autofill_fill_otp_from_sms">Извлечение кодов из SMS</string>
<string name="oreo_autofill_max_matches_reached">Достигнуто максимальное количество совпадений (%1$d); очистите совпадения перед тем как добавите новые.</string>
<string name="oreo_autofill_warning_publisher_header">Издатель приложения изменился с тех пор как вы первый раз связали с ним запись хранилища паролей:</string>
<string name="oreo_autofill_warning_publisher_footer"><b>Установленное приложение может попытаться украсть ваши учетные данные, выдавая себя за доверенное приложение</b>\n\nПопробуйте удалить или переустановить  приложение из доверенного источника, такого как Play Store, Amazon Appstore, F-Droid или магазин приложений производителя вашего смартфона.</string>
@@ -205,7 +203,7 @@
<string name="oreo_autofill_warning_publisher_changed_disable_autofill_button">Оставить автозаполнение отключенным</string>
<string name="oreo_autofill_warning_publisher_reenable_button">Включить автозаполнение снова</string>
<string name="oreo_autofill_warning_publisher_warning_sign_description">Предупреждение</string>
- <string name="oreo_autofill_warning_publisher_dataset_summary">Нажмите для получения подробностей...</string>
+ <string name="oreo_autofill_warning_publisher_dataset_summary">Нажмите для получения подробностей</string>
<string name="oreo_autofill_warning_publisher_dataset_title">Возможная попытка фишинга</string>
<string name="oreo_autofill_general_fill_and_save_support">Заполнить и сохранить учетные данные</string>
<string name="oreo_autofill_general_fill_support">Заполнить учетные данные</string>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index ea222409..9c87c1da 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -231,16 +231,17 @@
<string name="app_icon_hint">App icon</string>
<!-- Oreo Autofill -->
+ <string name="oreo_autofill_select_and_fill_into">Select entry to fill into</string>
<string name="oreo_autofill_strict_domain_search">Phishing-resistant search</string>
<string name="oreo_autofill_match_with">Match with %1$s</string>
<string name="oreo_autofill_matches_clear_existing">Clear existing matches</string>
<string name="oreo_autofill_filter_no_results">No results.</string>
- <string name="oreo_autofill_search_in_store">Search in store…</string>
+ <string name="oreo_autofill_search_in_store">Search entry</string>
<string name="oreo_autofill_save_internal_error">Save failed due to an internal error</string>
<string name="oreo_autofill_save_app_not_supported">This app is currently not supported</string>
<string name="oreo_autofill_save_passwords_dont_match">Passwords don\'t match</string>
- <string name="oreo_autofill_generate_password">Generate password…</string>
- <string name="oreo_autofill_fill_otp_from_sms">Extract code from SMS…</string>
+ <string name="oreo_autofill_generate_password">Create entry</string>
+ <string name="oreo_autofill_fill_otp_from_sms">Extract code from SMS</string>
<string name="oreo_autofill_max_matches_reached">Maximum number of matches (%1$d) reached; clear matches before adding new ones.</string>
<string name="oreo_autofill_warning_publisher_header">This app\'s publisher has changed since you first associated a Password Store entry with it:</string>
<string name="oreo_autofill_warning_publisher_footer"><b>The currently installed app may be trying to steal your credentials by pretending to be a trusted app.</b>\n\nTry to uninstall and reinstall the app from a trusted source, such as the Play Store, Amazon Appstore, F-Droid, or your phone manufacturer\'s store.</string>
@@ -250,7 +251,7 @@
<string name="oreo_autofill_warning_publisher_changed_disable_autofill_button">Keep Autofill disabled</string>
<string name="oreo_autofill_warning_publisher_reenable_button">Re-enable Autofill</string>
<string name="oreo_autofill_warning_publisher_warning_sign_description">Warning</string>
- <string name="oreo_autofill_warning_publisher_dataset_summary">Tap for details…</string>
+ <string name="oreo_autofill_warning_publisher_dataset_summary">Tap for details</string>
<string name="oreo_autofill_warning_publisher_dataset_title">Possible phishing attempt</string>
<string name="oreo_autofill_general_fill_and_save_support">Fill and save credentials</string>
<string name="oreo_autofill_general_fill_support">Fill credentials</string>
diff --git a/app/src/main/res/xml/oreo_autofill_service.xml b/app/src/main/res/xml/oreo_autofill_service.xml
index 00736cd5..b8a7510b 100644
--- a/app/src/main/res/xml/oreo_autofill_service.xml
+++ b/app/src/main/res/xml/oreo_autofill_service.xml
@@ -5,6 +5,7 @@
<autofill-service xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
+ android:supportsInlineSuggestions="true"
tools:ignore="UnusedAttribute">
<compatibility-package android:name="com.android.chrome" />
<compatibility-package android:name="com.brave.browser" />