mirror of
https://github.com/bitwarden/android.git
synced 2025-02-17 12:30:00 +03:00
BIT-1458: update overlay autofill (#634)
This commit is contained in:
parent
6220670ce3
commit
3f6a12740c
5 changed files with 341 additions and 41 deletions
|
@ -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()
|
||||
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue