BIT-1458: update overlay autofill (#634)

This commit is contained in:
Lucas Kivi 2024-01-16 11:43:13 -06:00 committed by Álison Fernandes
parent 6220670ce3
commit 3f6a12740c
5 changed files with 341 additions and 41 deletions

View file

@ -20,8 +20,8 @@ fun FilledPartition.buildDataset(
autofillAppInfo: AutofillAppInfo,
): Dataset {
val remoteViewsPlaceholder = buildAutofillRemoteViews(
packageName = autofillAppInfo.packageName,
title = autofillCipher.name,
autofillAppInfo = autofillAppInfo,
autofillCipher = autofillCipher,
)
val datasetBuilder = Dataset.Builder()

View file

@ -1,22 +1,69 @@
package com.x8bit.bitwarden.ui.autofill
import android.content.Context
import android.widget.RemoteViews
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.data.autofill.model.AutofillAppInfo
import com.x8bit.bitwarden.data.autofill.model.AutofillCipher
import com.x8bit.bitwarden.ui.autofill.util.isSystemDarkMode
/**
* Build [RemoteViews] for representing an autofill suggestion.
*/
fun buildAutofillRemoteViews(
packageName: String,
title: String,
autofillAppInfo: AutofillAppInfo,
autofillCipher: AutofillCipher,
): RemoteViews =
RemoteViews(
packageName,
autofillAppInfo.packageName,
R.layout.autofill_remote_view,
)
.apply {
setTextViewText(
R.id.text,
title,
R.id.title,
autofillCipher.name,
)
setTextViewText(
R.id.subtitle,
autofillCipher.subtitle,
)
setImageViewResource(
R.id.icon,
autofillCipher.iconRes,
)
setInt(
R.id.container,
"setBackgroundColor",
autofillAppInfo.context.surface,
)
setInt(
R.id.icon,
"setColorFilter",
autofillAppInfo.context.onSurface,
)
setInt(
R.id.title,
"setTextColor",
autofillAppInfo.context.onSurface,
)
setInt(
R.id.subtitle,
"setTextColor",
autofillAppInfo.context.onSurfaceVariant,
)
}
private val Context.onSurface: Int
get() = getColor(
if (isSystemDarkMode) R.color.dark_on_surface else R.color.on_surface,
)
private val Context.onSurfaceVariant: Int
get() = getColor(
if (isSystemDarkMode) R.color.dark_on_surface_variant else R.color.on_surface_variant,
)
private val Context.surface: Int
get() = getColor(
if (isSystemDarkMode) R.color.dark_surface else R.color.surface,
)

View file

@ -1,6 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp" />
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:paddingLeft="10dp"
android:paddingTop="5dp"
android:paddingRight="10dp"
android:paddingBottom="5dp">
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginEnd="10dp"
android:adjustViewBounds="true"
android:contentDescription="@null"
android:maxWidth="20dp"
android:maxHeight="20dp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical" />
<TextView
android:id="@+id/subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical" />
</LinearLayout>
</LinearLayout>

View file

@ -81,8 +81,8 @@ class FilledPartitionExtensionsTest {
val inlinePresentation: InlinePresentation = mockk()
every {
buildAutofillRemoteViews(
packageName = PACKAGE_NAME,
title = CIPHER_NAME,
autofillAppInfo = autofillAppInfo,
autofillCipher = autofillCipher,
)
} returns remoteViews
every {
@ -110,8 +110,8 @@ class FilledPartitionExtensionsTest {
assertEquals(dataset, actual)
verify(exactly = 1) {
buildAutofillRemoteViews(
packageName = PACKAGE_NAME,
title = CIPHER_NAME,
autofillAppInfo = autofillAppInfo,
autofillCipher = autofillCipher,
)
inlinePresentationSpec.createCipherInlinePresentationOrNull(
autofillAppInfo = autofillAppInfo,
@ -137,11 +137,10 @@ class FilledPartitionExtensionsTest {
packageName = PACKAGE_NAME,
sdkInt = 18,
)
val inlinePresentation: InlinePresentation = mockk()
every {
buildAutofillRemoteViews(
packageName = PACKAGE_NAME,
title = CIPHER_NAME,
autofillAppInfo = autofillAppInfo,
autofillCipher = autofillCipher,
)
} returns remoteViews
every {
@ -160,8 +159,8 @@ class FilledPartitionExtensionsTest {
assertEquals(dataset, actual)
verify(exactly = 1) {
buildAutofillRemoteViews(
packageName = PACKAGE_NAME,
title = CIPHER_NAME,
autofillAppInfo = autofillAppInfo,
autofillCipher = autofillCipher,
)
filledItem.applyToDatasetPreTiramisu(
datasetBuilder = any(),
@ -183,8 +182,8 @@ class FilledPartitionExtensionsTest {
val inlinePresentation: InlinePresentation = mockk()
every {
buildAutofillRemoteViews(
packageName = PACKAGE_NAME,
title = CIPHER_NAME,
autofillAppInfo = autofillAppInfo,
autofillCipher = autofillCipher,
)
} returns remoteViews
every {
@ -210,8 +209,8 @@ class FilledPartitionExtensionsTest {
assertEquals(dataset, actual)
verify(exactly = 1) {
buildAutofillRemoteViews(
packageName = PACKAGE_NAME,
title = CIPHER_NAME,
autofillAppInfo = autofillAppInfo,
autofillCipher = autofillCipher,
)
inlinePresentationSpec.createCipherInlinePresentationOrNull(
autofillAppInfo = autofillAppInfo,

View file

@ -1,52 +1,119 @@
package com.x8bit.bitwarden.ui.autofill
import android.content.res.Resources
import android.content.Context
import android.widget.RemoteViews
import com.x8bit.bitwarden.R
import io.mockk.confirmVerified
import com.x8bit.bitwarden.data.autofill.model.AutofillAppInfo
import com.x8bit.bitwarden.data.autofill.model.AutofillCipher
import com.x8bit.bitwarden.ui.autofill.util.isSystemDarkMode
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.mockkConstructor
import io.mockk.mockkStatic
import io.mockk.runs
import io.mockk.unmockkConstructor
import io.mockk.unmockkStatic
import io.mockk.verify
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
class BitwardenRemoteViewsTest {
private val testResources: Resources = mockk()
private val testContext: Context = mockk {
every { this@mockk.getColor(R.color.dark_on_surface) } returns DARK_ON_SURFACE_COLOR
every {
this@mockk.getColor(R.color.dark_on_surface_variant)
} returns DARK_ON_SURFACE_VARIANT_COLOR
every { this@mockk.getColor(R.color.dark_surface) } returns DARK_SURFACE_COLOR
every { this@mockk.getColor(R.color.on_surface) } returns ON_SURFACE_COLOR
every { this@mockk.getColor(R.color.on_surface_variant) } returns ON_SURFACE_VARIANT_COLOR
every { this@mockk.getColor(R.color.surface) } returns SURFACE_COLOR
}
private val autofillAppInfo: AutofillAppInfo = mockk {
every { this@mockk.context } returns testContext
every { this@mockk.packageName } returns PACKAGE_NAME
}
private val autofillCipher: AutofillCipher = mockk {
every { this@mockk.iconRes } returns ICON_RES
every { this@mockk.name } returns NAME
every { this@mockk.subtitle } returns SUBTITLE
}
@BeforeEach
fun setup() {
mockkStatic(Context::isSystemDarkMode)
mockkConstructor(RemoteViews::class)
}
@AfterEach
fun teardown() {
unmockkStatic(Context::isSystemDarkMode)
unmockkConstructor(RemoteViews::class)
confirmVerified(
testResources,
)
}
@Test
fun `buildAutofillRemoteViews should set text`() {
fun `buildAutofillRemoteViews should set values and light mode colors when not night mode`() {
// Setup
val title = "Bitwarden"
every { testContext.isSystemDarkMode } returns false
every {
anyConstructed<RemoteViews>()
.setTextViewText(
R.id.text,
title,
R.id.title,
NAME,
)
} just runs
every {
anyConstructed<RemoteViews>()
.setTextViewText(
R.id.subtitle,
SUBTITLE,
)
} just runs
every {
anyConstructed<RemoteViews>()
.setImageViewResource(
R.id.icon,
ICON_RES,
)
} just runs
every {
anyConstructed<RemoteViews>()
.setInt(
R.id.container,
"setBackgroundColor",
SURFACE_COLOR,
)
} just runs
every {
anyConstructed<RemoteViews>()
.setInt(
R.id.icon,
"setColorFilter",
ON_SURFACE_COLOR,
)
} just runs
every {
anyConstructed<RemoteViews>()
.setInt(
R.id.title,
"setTextColor",
ON_SURFACE_COLOR,
)
} just runs
every {
anyConstructed<RemoteViews>()
.setInt(
R.id.subtitle,
"setTextColor",
ON_SURFACE_VARIANT_COLOR,
)
} just runs
// Test
buildAutofillRemoteViews(
title = title,
packageName = PACKAGE_NAME,
autofillAppInfo = autofillAppInfo,
autofillCipher = autofillCipher,
)
// Note: impossible to do a useful test of the returned RemoteViews due to mockking
@ -57,13 +124,166 @@ class BitwardenRemoteViewsTest {
verify(exactly = 1) {
anyConstructed<RemoteViews>()
.setTextViewText(
R.id.text,
title,
R.id.title,
NAME,
)
anyConstructed<RemoteViews>()
.setTextViewText(
R.id.subtitle,
SUBTITLE,
)
anyConstructed<RemoteViews>()
.setImageViewResource(
R.id.icon,
ICON_RES,
)
anyConstructed<RemoteViews>()
.setInt(
R.id.container,
"setBackgroundColor",
SURFACE_COLOR,
)
anyConstructed<RemoteViews>()
.setInt(
R.id.icon,
"setColorFilter",
ON_SURFACE_COLOR,
)
anyConstructed<RemoteViews>()
.setInt(
R.id.title,
"setTextColor",
ON_SURFACE_COLOR,
)
anyConstructed<RemoteViews>()
.setInt(
R.id.subtitle,
"setTextColor",
ON_SURFACE_VARIANT_COLOR,
)
}
}
companion object {
private const val PACKAGE_NAME: String = "com.x8bit.bitwarden"
@Test
fun `buildAutofillRemoteViews should set values and dark mode colors when night mode`() {
// Setup
every { testContext.isSystemDarkMode } returns true
every {
anyConstructed<RemoteViews>()
.setTextViewText(
R.id.title,
NAME,
)
} just runs
every {
anyConstructed<RemoteViews>()
.setTextViewText(
R.id.subtitle,
SUBTITLE,
)
} just runs
every {
anyConstructed<RemoteViews>()
.setImageViewResource(
R.id.icon,
ICON_RES,
)
} just runs
every {
anyConstructed<RemoteViews>()
.setInt(
R.id.container,
"setBackgroundColor",
DARK_SURFACE_COLOR,
)
} just runs
every {
anyConstructed<RemoteViews>()
.setInt(
R.id.icon,
"setColorFilter",
DARK_ON_SURFACE_COLOR,
)
} just runs
every {
anyConstructed<RemoteViews>()
.setInt(
R.id.title,
"setTextColor",
DARK_ON_SURFACE_COLOR,
)
} just runs
every {
anyConstructed<RemoteViews>()
.setInt(
R.id.subtitle,
"setTextColor",
DARK_ON_SURFACE_VARIANT_COLOR,
)
} just runs
// Test
buildAutofillRemoteViews(
autofillAppInfo = autofillAppInfo,
autofillCipher = autofillCipher,
)
// Note: impossible to do a useful test of the returned RemoteViews due to mockking
// constraints of the [RemoteViews] constructor. Our best bet is to make sure the correct
// operations are performed on the constructed [RemoteViews].
// Verify
verify(exactly = 1) {
anyConstructed<RemoteViews>()
.setTextViewText(
R.id.title,
NAME,
)
anyConstructed<RemoteViews>()
.setTextViewText(
R.id.subtitle,
SUBTITLE,
)
anyConstructed<RemoteViews>()
.setImageViewResource(
R.id.icon,
ICON_RES,
)
anyConstructed<RemoteViews>()
.setInt(
R.id.container,
"setBackgroundColor",
DARK_SURFACE_COLOR,
)
anyConstructed<RemoteViews>()
.setInt(
R.id.icon,
"setColorFilter",
DARK_ON_SURFACE_COLOR,
)
anyConstructed<RemoteViews>()
.setInt(
R.id.title,
"setTextColor",
DARK_ON_SURFACE_COLOR,
)
anyConstructed<RemoteViews>()
.setInt(
R.id.subtitle,
"setTextColor",
DARK_ON_SURFACE_VARIANT_COLOR,
)
}
}
}
private const val DARK_ON_SURFACE_COLOR: Int = 321
private const val DARK_ON_SURFACE_VARIANT_COLOR: Int = 654
private const val DARK_SURFACE_COLOR: Int = 987
private const val ICON_RES: Int = 41421421
private const val NAME: String = "NAME"
private const val ON_SURFACE_COLOR: Int = 123
private const val ON_SURFACE_VARIANT_COLOR: Int = 456
private const val PACKAGE_NAME: String = "com.x8bit.bitwarden"
private const val SUBTITLE: String = "SUBTITLE"
private const val SURFACE_COLOR: Int = 789