mirror of
https://github.com/bitwarden/android.git
synced 2026-03-17 10:04:05 -05:00
Make issuer required and account name optional (#55)
This commit is contained in:
@@ -2,11 +2,11 @@
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 1,
|
||||
"identityHash": "8724b95439edde85bd15e0bd2e02195e",
|
||||
"identityHash": "480a4540e7704429515a28eb9c38ab14",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "items",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `key` TEXT NOT NULL, `accountName` TEXT NOT NULL, `type` TEXT NOT NULL, `algorithm` TEXT NOT NULL, `period` INTEGER NOT NULL, `digits` INTEGER NOT NULL, `issuer` TEXT, `userId` TEXT, PRIMARY KEY(`id`))",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `key` TEXT NOT NULL, `type` TEXT NOT NULL, `algorithm` TEXT NOT NULL, `period` INTEGER NOT NULL, `digits` INTEGER NOT NULL, `issuer` TEXT NOT NULL, `userId` TEXT, `accountName` TEXT, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
@@ -20,12 +20,6 @@
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "accountName",
|
||||
"columnName": "accountName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
@@ -54,13 +48,19 @@
|
||||
"fieldPath": "issuer",
|
||||
"columnName": "issuer",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "userId",
|
||||
"columnName": "userId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "accountName",
|
||||
"columnName": "accountName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
@@ -76,7 +76,7 @@
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '8724b95439edde85bd15e0bd2e02195e')"
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '480a4540e7704429515a28eb9c38ab14')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.bitwarden.authenticator.data.authenticator.datasource.disk.entity
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.core.text.htmlEncode
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
@@ -16,9 +18,6 @@ data class AuthenticatorItemEntity(
|
||||
@ColumnInfo(name = "key")
|
||||
val key: String,
|
||||
|
||||
@ColumnInfo(name = "accountName")
|
||||
val accountName: String,
|
||||
|
||||
@ColumnInfo(name = "type")
|
||||
val type: AuthenticatorItemType = AuthenticatorItemType.TOTP,
|
||||
|
||||
@@ -32,8 +31,42 @@ data class AuthenticatorItemEntity(
|
||||
val digits: Int = 6,
|
||||
|
||||
@ColumnInfo(name = "issuer")
|
||||
val issuer: String? = null,
|
||||
val issuer: String,
|
||||
|
||||
@ColumnInfo(name = "userId")
|
||||
val userId: String? = null,
|
||||
)
|
||||
|
||||
@ColumnInfo(name = "accountName")
|
||||
val accountName: String? = null,
|
||||
) {
|
||||
fun toOtpAuthUriString(): String {
|
||||
return when (type) {
|
||||
AuthenticatorItemType.TOTP -> {
|
||||
val label = if (accountName.isNullOrBlank()) {
|
||||
issuer
|
||||
} else {
|
||||
"$issuer:$accountName"
|
||||
}
|
||||
Uri.Builder()
|
||||
.scheme("otpauth")
|
||||
.authority("totp")
|
||||
.appendPath(label.htmlEncode())
|
||||
.appendQueryParameter("secret", key)
|
||||
.appendQueryParameter("algorithm", algorithm.name)
|
||||
.appendQueryParameter("digits", digits.toString())
|
||||
.appendQueryParameter("period", period.toString())
|
||||
.appendQueryParameter("issuer", issuer)
|
||||
.build()
|
||||
.toString()
|
||||
}
|
||||
|
||||
AuthenticatorItemType.STEAM -> {
|
||||
if (key.startsWith("steam://")) {
|
||||
key
|
||||
} else {
|
||||
"steam://$key"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ class TotpCodeManagerImpl @Inject constructor(
|
||||
|
||||
return mutableVerificationCodeStateFlowMap.getOrPut(itemEntity) {
|
||||
flow<DataState<VerificationCodeItem?>> {
|
||||
val totpCode = itemEntity.key
|
||||
val totpCode = itemEntity.toOtpAuthUriString()
|
||||
|
||||
var item: VerificationCodeItem? = null
|
||||
while (currentCoroutineContext().isActive) {
|
||||
|
||||
@@ -36,16 +36,10 @@ data class ExportJsonData(
|
||||
* This model is loosely based off of Bitwarden's Cipher.Login JSON.
|
||||
*
|
||||
* @property totp OTP secret used to generate a verification code.
|
||||
* @property issuer Optional issuer of the 2fa code.
|
||||
* @property period Optional refresh period in seconds. Default is 30.
|
||||
* @property digits Optional number of digits in the verification code. Default is 6
|
||||
*/
|
||||
@Serializable
|
||||
data class ItemLoginData(
|
||||
val totp: String,
|
||||
val issuer: String?,
|
||||
val period: Int,
|
||||
val digits: Int,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ data class VerificationCodeItem(
|
||||
val issuer: String?,
|
||||
) {
|
||||
/**
|
||||
* The composite label of the authenticator item.
|
||||
* The composite label of the authenticator item. Used for constructing an OTPAuth URI.
|
||||
* ```
|
||||
* label = issuer (“:” / “%3A”) *”%20” username
|
||||
* ```
|
||||
|
||||
@@ -233,7 +233,7 @@ class AuthenticatorRepositoryImpl @Inject constructor(
|
||||
|
||||
private suspend fun encodeVaultDataToCsv(fileUri: Uri): ExportDataResult {
|
||||
val headerLine =
|
||||
"folder,favorite,type,name,login_uri,login_totp,issuer,period,digits"
|
||||
"folder,favorite,type,name,login_uri,login_totp"
|
||||
val dataLines = authenticatorDiskSource
|
||||
.getItems()
|
||||
.firstOrNull()
|
||||
@@ -250,7 +250,7 @@ class AuthenticatorRepositoryImpl @Inject constructor(
|
||||
}
|
||||
|
||||
private fun AuthenticatorItemEntity.toCsvFormat() =
|
||||
",,1,$accountName,,$key,$issuer,$period,$digits"
|
||||
",,1,$issuer,,${toOtpAuthUriString()},$issuer,$period,$digits"
|
||||
|
||||
private suspend fun encodeVaultDataToJson(fileUri: Uri): ExportDataResult {
|
||||
val dataString: String = Json.encodeToString(
|
||||
@@ -281,14 +281,11 @@ class AuthenticatorRepositoryImpl @Inject constructor(
|
||||
folderId = null,
|
||||
organizationId = null,
|
||||
collectionIds = null,
|
||||
name = accountName,
|
||||
name = issuer,
|
||||
notes = null,
|
||||
type = 1,
|
||||
login = ExportJsonData.ExportItem.ItemLoginData(
|
||||
totp = key,
|
||||
issuer = issuer,
|
||||
period = period,
|
||||
digits = digits,
|
||||
totp = toOtpAuthUriString(),
|
||||
),
|
||||
favorite = false,
|
||||
)
|
||||
|
||||
@@ -16,12 +16,12 @@ import com.bitwarden.authenticator.data.authenticator.datasource.disk.entity.Aut
|
||||
*/
|
||||
data class UpdateItemRequest(
|
||||
val key: String,
|
||||
val accountName: String,
|
||||
val accountName: String?,
|
||||
val type: AuthenticatorItemType,
|
||||
val algorithm: AuthenticatorItemAlgorithm,
|
||||
val period: Int,
|
||||
val digits: Int,
|
||||
val issuer: String?,
|
||||
val issuer: String,
|
||||
) {
|
||||
/**
|
||||
* The composite label of the authenticator item. Derived from combining [issuer] and [accountName]
|
||||
@@ -29,9 +29,9 @@ data class UpdateItemRequest(
|
||||
* label = accountName /issuer (“:” / “%3A”) *”%20” accountName
|
||||
* ```
|
||||
*/
|
||||
val label = if (issuer != null) {
|
||||
"$issuer:$accountName"
|
||||
val label = if (accountName.isNullOrBlank()) {
|
||||
issuer
|
||||
} else {
|
||||
accountName
|
||||
"$issuer:$accountName"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,17 +139,17 @@ fun EditItemScreen(
|
||||
.imePadding()
|
||||
.padding(innerPadding),
|
||||
viewState = viewState,
|
||||
onAccountNameTextChange = remember(viewModel) {
|
||||
onIssuerNameTextChange = remember(viewModel) {
|
||||
{
|
||||
viewModel.trySendAction(
|
||||
EditItemAction.AccountNameTextChange(it)
|
||||
EditItemAction.IssuerNameTextChange(it)
|
||||
)
|
||||
}
|
||||
},
|
||||
onIssuerTextChange = remember(viewModel) {
|
||||
onUsernameTextChange = remember(viewModel) {
|
||||
{
|
||||
viewModel.trySendAction(
|
||||
EditItemAction.IssuerTextChange(it)
|
||||
EditItemAction.UsernameTextChange(it)
|
||||
)
|
||||
}
|
||||
},
|
||||
@@ -211,8 +211,8 @@ fun EditItemScreen(
|
||||
fun EditItemContent(
|
||||
modifier: Modifier = Modifier,
|
||||
viewState: EditItemState.ViewState.Content,
|
||||
onAccountNameTextChange: (String) -> Unit = {},
|
||||
onIssuerTextChange: (String) -> Unit = {},
|
||||
onIssuerNameTextChange: (String) -> Unit = {},
|
||||
onUsernameTextChange: (String) -> Unit = {},
|
||||
onTypeOptionClicked: (AuthenticatorItemType) -> Unit = {},
|
||||
onTotpCodeTextChange: (String) -> Unit = {},
|
||||
onAlgorithmOptionClicked: (AuthenticatorItemAlgorithm) -> Unit = {},
|
||||
@@ -237,8 +237,8 @@ fun EditItemContent(
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = 16.dp),
|
||||
label = stringResource(id = R.string.name),
|
||||
value = viewState.itemData.accountName,
|
||||
onValueChange = onAccountNameTextChange,
|
||||
value = viewState.itemData.issuer,
|
||||
onValueChange = onIssuerNameTextChange,
|
||||
singleLine = true,
|
||||
)
|
||||
}
|
||||
@@ -256,20 +256,18 @@ fun EditItemContent(
|
||||
)
|
||||
}
|
||||
|
||||
viewState.itemData.issuer?.let { issuer ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
label = stringResource(id = R.string.account_info),
|
||||
value = issuer,
|
||||
onValueChange = onIssuerTextChange,
|
||||
label = stringResource(id = R.string.username),
|
||||
value = viewState.itemData.username.orEmpty(),
|
||||
onValueChange = onUsernameTextChange,
|
||||
singleLine = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
@@ -483,7 +481,7 @@ private fun EditItemContentExpandedOptionsPreview() {
|
||||
refreshPeriod = AuthenticatorRefreshPeriodOption.THIRTY,
|
||||
totpCode = "123456",
|
||||
type = AuthenticatorItemType.TOTP,
|
||||
accountName = "account name",
|
||||
username = "account name",
|
||||
issuer = "issuer",
|
||||
algorithm = AuthenticatorItemAlgorithm.SHA1,
|
||||
digits = VerificationCodeDigitsOption.SIX
|
||||
@@ -502,7 +500,7 @@ private fun EditItemContentCollapsedOptionsPreview() {
|
||||
refreshPeriod = AuthenticatorRefreshPeriodOption.THIRTY,
|
||||
totpCode = "123456",
|
||||
type = AuthenticatorItemType.TOTP,
|
||||
accountName = "account name",
|
||||
username = "account name",
|
||||
issuer = "issuer",
|
||||
algorithm = AuthenticatorItemAlgorithm.SHA1,
|
||||
digits = VerificationCodeDigitsOption.SIX
|
||||
|
||||
@@ -57,8 +57,8 @@ class EditItemViewModel @Inject constructor(
|
||||
is EditItemAction.AlgorithmOptionClick -> handleAlgorithmOptionClick(action)
|
||||
is EditItemAction.CancelClick -> handleCancelClick()
|
||||
is EditItemAction.TypeOptionClick -> handleTypeOptionClick(action)
|
||||
is EditItemAction.AccountNameTextChange -> handleAccountNameTextChange(action)
|
||||
is EditItemAction.IssuerTextChange -> handleIssuerTextChange(action)
|
||||
is EditItemAction.IssuerNameTextChange -> handleIssuerNameTextChange(action)
|
||||
is EditItemAction.UsernameTextChange -> handleIssuerTextChange(action)
|
||||
is EditItemAction.RefreshPeriodOptionClick -> handlePeriodTextChange(action)
|
||||
is EditItemAction.TotpCodeTextChange -> handleTotpCodeTextChange(action)
|
||||
is EditItemAction.NumberOfDigitsOptionClick -> handleNumberOfDigitsOptionChange(action)
|
||||
@@ -76,7 +76,7 @@ class EditItemViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
private fun handleSaveClick() = onContent { content ->
|
||||
if (content.itemData.accountName.isBlank()) {
|
||||
if (content.itemData.issuer.isBlank()) {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
dialog = EditItemState.DialogState.Generic(
|
||||
@@ -110,12 +110,12 @@ class EditItemViewModel @Inject constructor(
|
||||
AuthenticatorItemEntity(
|
||||
id = state.itemId,
|
||||
key = content.itemData.totpCode.trim(),
|
||||
accountName = content.itemData.accountName.trim(),
|
||||
accountName = content.itemData.username?.trim(),
|
||||
type = content.itemData.type,
|
||||
algorithm = content.itemData.algorithm,
|
||||
period = content.itemData.refreshPeriod.seconds,
|
||||
digits = content.itemData.digits.length,
|
||||
issuer = content.itemData.issuer?.trim(),
|
||||
issuer = content.itemData.issuer.trim(),
|
||||
)
|
||||
)
|
||||
trySendAction(EditItemAction.Internal.UpdateItemResult(result))
|
||||
@@ -130,18 +130,18 @@ class EditItemViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleAccountNameTextChange(action: EditItemAction.AccountNameTextChange) {
|
||||
private fun handleIssuerNameTextChange(action: EditItemAction.IssuerNameTextChange) {
|
||||
updateItemData { currentItemData ->
|
||||
currentItemData.copy(
|
||||
accountName = action.accountName
|
||||
issuer = action.issuerName
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleIssuerTextChange(action: EditItemAction.IssuerTextChange) {
|
||||
private fun handleIssuerTextChange(action: EditItemAction.UsernameTextChange) {
|
||||
updateItemData { currentItemData ->
|
||||
currentItemData.copy(
|
||||
issuer = action.issue
|
||||
username = action.username
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -318,7 +318,7 @@ class EditItemViewModel @Inject constructor(
|
||||
?: AuthenticatorRefreshPeriodOption.THIRTY,
|
||||
totpCode = key,
|
||||
type = type,
|
||||
accountName = accountName,
|
||||
username = accountName,
|
||||
issuer = issuer,
|
||||
algorithm = algorithm,
|
||||
digits = VerificationCodeDigitsOption.fromIntOrNull(digits)
|
||||
@@ -435,12 +435,12 @@ sealed class EditItemAction {
|
||||
/**
|
||||
* The user has changed the account name text.
|
||||
*/
|
||||
data class AccountNameTextChange(val accountName: String) : EditItemAction()
|
||||
data class IssuerNameTextChange(val issuerName: String) : EditItemAction()
|
||||
|
||||
/**
|
||||
* The user has changed the issue text.
|
||||
*/
|
||||
data class IssuerTextChange(val issue: String) : EditItemAction()
|
||||
data class UsernameTextChange(val username: String) : EditItemAction()
|
||||
|
||||
/**
|
||||
* The user has selected an Item Type option.
|
||||
|
||||
@@ -12,7 +12,7 @@ import kotlinx.parcelize.Parcelize
|
||||
*
|
||||
* @property refreshPeriod The period for the verification code.
|
||||
* @property totpCode The totp code for the item.
|
||||
* @property accountName Account or username for this item.
|
||||
* @property username Account or username for this item.
|
||||
* @property issuer Name of the item provider.
|
||||
* @property algorithm Hashing algorithm used with the item.
|
||||
* @property digits Number of digits in the verification code.
|
||||
@@ -22,8 +22,8 @@ data class EditItemData(
|
||||
val refreshPeriod: AuthenticatorRefreshPeriodOption,
|
||||
val totpCode: String,
|
||||
val type: AuthenticatorItemType,
|
||||
val accountName: String,
|
||||
val issuer: String?,
|
||||
val username: String?,
|
||||
val issuer: String,
|
||||
val algorithm: AuthenticatorItemAlgorithm,
|
||||
val digits: VerificationCodeDigitsOption,
|
||||
) : Parcelable
|
||||
|
||||
@@ -31,6 +31,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
@@ -228,7 +229,8 @@ fun ItemListingScreen(
|
||||
items(currentState.itemList) {
|
||||
VaultVerificationCodeItem(
|
||||
authCode = it.authCode,
|
||||
issuer = it.issuer,
|
||||
name = it.issuer,
|
||||
username = it.username,
|
||||
periodSeconds = it.periodSeconds,
|
||||
timeLeftSeconds = it.timeLeftSeconds,
|
||||
alertThresholdSeconds = it.alertThresholdSeconds,
|
||||
@@ -257,7 +259,6 @@ fun ItemListingScreen(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
supportingLabel = it.supportingLabel,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,6 +391,13 @@ class ItemListingViewModel @Inject constructor(
|
||||
?: return null
|
||||
|
||||
val label = uri.pathSegments.firstOrNull() ?: return null
|
||||
val accountName = if (label.contains(":")) {
|
||||
label
|
||||
.split(":")
|
||||
.last()
|
||||
} else {
|
||||
label
|
||||
}
|
||||
|
||||
val key = uri.getQueryParameter(SECRET) ?: return null
|
||||
|
||||
@@ -401,14 +408,14 @@ class ItemListingViewModel @Inject constructor(
|
||||
|
||||
val digits = uri.getQueryParameter(DIGITS)?.toInt() ?: 6
|
||||
|
||||
val issuer = uri.getQueryParameter(ISSUER)
|
||||
val issuer = uri.getQueryParameter(ISSUER) ?: label
|
||||
|
||||
val period = uri.getQueryParameter(PERIOD)?.toInt() ?: 30
|
||||
|
||||
return AuthenticatorItemEntity(
|
||||
id = UUID.randomUUID().toString(),
|
||||
key = key,
|
||||
accountName = label,
|
||||
accountName = accountName,
|
||||
type = type,
|
||||
algorithm = algorithm,
|
||||
period = period,
|
||||
|
||||
@@ -39,20 +39,21 @@ import com.bitwarden.authenticator.ui.platform.theme.AuthenticatorTheme
|
||||
* The verification code item displayed to the user.
|
||||
*
|
||||
* @param authCode The code for the item.
|
||||
* @param issuer The label for the item.
|
||||
* @param name The label for the item. Represents the OTP issuer.
|
||||
* @param username The supporting label for the item. Represents the OTP account name.
|
||||
* @param periodSeconds The times span where the code is valid.
|
||||
* @param timeLeftSeconds The seconds remaining until a new code is needed.
|
||||
* @param startIcon The leading icon for the item.
|
||||
* @param onItemClick The lambda function to be invoked when the item is clicked.
|
||||
* @param modifier The modifier for the item.
|
||||
* @param supportingLabel The supporting label for the item.
|
||||
*/
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Suppress("LongMethod", "MagicNumber")
|
||||
@Composable
|
||||
fun VaultVerificationCodeItem(
|
||||
authCode: String,
|
||||
issuer: String?,
|
||||
name: String?,
|
||||
username: String?,
|
||||
periodSeconds: Int,
|
||||
timeLeftSeconds: Int,
|
||||
alertThresholdSeconds: Int,
|
||||
@@ -61,7 +62,6 @@ fun VaultVerificationCodeItem(
|
||||
onEditItemClick: () -> Unit,
|
||||
onDeleteItemClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
supportingLabel: String? = null,
|
||||
) {
|
||||
var shouldShowDropdownMenu by remember { mutableStateOf(value = false) }
|
||||
Box(modifier = modifier) {
|
||||
@@ -93,9 +93,9 @@ fun VaultVerificationCodeItem(
|
||||
verticalArrangement = Arrangement.SpaceEvenly,
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
issuer?.let {
|
||||
if (!name.isNullOrEmpty()) {
|
||||
Text(
|
||||
text = it,
|
||||
text = name,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
maxLines = 1,
|
||||
@@ -103,9 +103,9 @@ fun VaultVerificationCodeItem(
|
||||
)
|
||||
}
|
||||
|
||||
supportingLabel?.let {
|
||||
if (!username.isNullOrEmpty()) {
|
||||
Text(
|
||||
text = it,
|
||||
text = username,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
maxLines = 1,
|
||||
@@ -173,7 +173,8 @@ private fun VerificationCodeItem_preview() {
|
||||
AuthenticatorTheme {
|
||||
VaultVerificationCodeItem(
|
||||
authCode = "1234567890".chunked(3).joinToString(" "),
|
||||
issuer = "Sample Label",
|
||||
name = "Issuer, AKA Name",
|
||||
username = "username@bitwarden.com",
|
||||
periodSeconds = 30,
|
||||
timeLeftSeconds = 15,
|
||||
alertThresholdSeconds = 7,
|
||||
@@ -182,7 +183,6 @@ private fun VerificationCodeItem_preview() {
|
||||
onEditItemClick = {},
|
||||
onDeleteItemClick = {},
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
supportingLabel = "Supporting Label",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,8 @@ import kotlinx.parcelize.Parcelize
|
||||
@Parcelize
|
||||
data class VerificationCodeDisplayItem(
|
||||
val id: String,
|
||||
val label: String,
|
||||
val issuer: String?,
|
||||
val supportingLabel: String?,
|
||||
val username: String?,
|
||||
val timeLeftSeconds: Int,
|
||||
val periodSeconds: Int,
|
||||
val alertThresholdSeconds: Int,
|
||||
|
||||
@@ -18,9 +18,8 @@ fun List<VerificationCodeItem>.toViewState(
|
||||
fun VerificationCodeItem.toDisplayItem(alertThresholdSeconds: Int) =
|
||||
VerificationCodeDisplayItem(
|
||||
id = id,
|
||||
label = label,
|
||||
issuer = issuer,
|
||||
supportingLabel = username,
|
||||
username = username,
|
||||
timeLeftSeconds = timeLeftSeconds,
|
||||
periodSeconds = periodSeconds,
|
||||
alertThresholdSeconds = alertThresholdSeconds,
|
||||
|
||||
@@ -133,7 +133,7 @@ fun ManualCodeEntryScreen(
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.name),
|
||||
value = state.accountName,
|
||||
value = state.issuer,
|
||||
onValueChange = remember(viewModel) {
|
||||
{
|
||||
viewModel.trySendAction(
|
||||
|
||||
@@ -5,6 +5,7 @@ import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.bitwarden.authenticator.R
|
||||
import com.bitwarden.authenticator.data.authenticator.datasource.disk.entity.AuthenticatorItemEntity
|
||||
import com.bitwarden.authenticator.data.authenticator.datasource.disk.entity.AuthenticatorItemType
|
||||
import com.bitwarden.authenticator.data.authenticator.repository.AuthenticatorRepository
|
||||
import com.bitwarden.authenticator.data.authenticator.repository.model.CreateItemResult
|
||||
import com.bitwarden.authenticator.ui.platform.base.BaseViewModel
|
||||
@@ -29,7 +30,7 @@ class ManualCodeEntryViewModel @Inject constructor(
|
||||
private val authenticatorRepository: AuthenticatorRepository,
|
||||
) : BaseViewModel<ManualCodeEntryState, ManualCodeEntryEvent, ManualCodeEntryAction>(
|
||||
initialState = savedStateHandle[KEY_STATE]
|
||||
?: ManualCodeEntryState(code = "", accountName = "", dialog = null),
|
||||
?: ManualCodeEntryState(code = "", issuer = "", dialog = null),
|
||||
) {
|
||||
override fun handleAction(action: ManualCodeEntryAction) {
|
||||
when (action) {
|
||||
@@ -47,7 +48,7 @@ class ManualCodeEntryViewModel @Inject constructor(
|
||||
|
||||
private fun handleIssuerTextChange(action: ManualCodeEntryAction.IssuerTextChange) {
|
||||
mutableStateFlow.update {
|
||||
it.copy(accountName = action.accountName)
|
||||
it.copy(issuer = action.issuer)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,8 +68,14 @@ class ManualCodeEntryViewModel @Inject constructor(
|
||||
AuthenticatorItemEntity(
|
||||
id = UUID.randomUUID().toString(),
|
||||
key = state.code,
|
||||
accountName = state.accountName,
|
||||
issuer = state.issuer,
|
||||
accountName = "",
|
||||
userId = null,
|
||||
type = if (state.code.startsWith("steam://")) {
|
||||
AuthenticatorItemType.STEAM
|
||||
} else {
|
||||
AuthenticatorItemType.TOTP
|
||||
}
|
||||
)
|
||||
)
|
||||
sendAction(ManualCodeEntryAction.Internal.CreateItemResultReceive(result))
|
||||
@@ -118,7 +125,7 @@ class ManualCodeEntryViewModel @Inject constructor(
|
||||
@Parcelize
|
||||
data class ManualCodeEntryState(
|
||||
val code: String,
|
||||
val accountName: String,
|
||||
val issuer: String,
|
||||
val dialog: DialogState?,
|
||||
) : Parcelable {
|
||||
|
||||
@@ -187,7 +194,7 @@ sealed class ManualCodeEntryAction {
|
||||
/**
|
||||
* The use has changed the issuer text.
|
||||
*/
|
||||
data class IssuerTextChange(val accountName: String) : ManualCodeEntryAction()
|
||||
data class IssuerTextChange(val issuer: String) : ManualCodeEntryAction()
|
||||
|
||||
/**
|
||||
* Models actions that the [ManualCodeEntryViewModel] itself might send.
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
<string name="add_code">Add code</string>
|
||||
<string name="authenticator_key_read_error">Cannot read authenticator key.</string>
|
||||
<string name="verification_code_added">Verification code added</string>
|
||||
<string name="account_info">Account info</string>
|
||||
<string name="username">Username</string>
|
||||
<string name="refresh_period">Refresh period</string>
|
||||
<string name="algorithm">Algorithm</string>
|
||||
<string name="hide">Hide</string>
|
||||
|
||||
Reference in New Issue
Block a user