diff --git a/app/build.gradle.kts b/app/build.gradle.kts index b63064beab..9878691fb8 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -4,6 +4,7 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { alias(libs.plugins.android.application) alias(libs.plugins.crashlytics) + alias(libs.plugins.detekt) alias(libs.plugins.hilt) alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.compose.compiler) @@ -144,6 +145,14 @@ dependencies { testImplementation(libs.square.turbine) androidTestImplementation(libs.bundles.tests.instrumented) + + detektPlugins(libs.detekt.detekt.formatting) + detektPlugins(libs.detekt.detekt.rules) +} + +detekt { + config.from(files("$rootDir/detekt-config.yml")) + baseline = file("$rootDir/detekt-baseline.xml") } kover { diff --git a/detekt-baseline.xml b/detekt-baseline.xml new file mode 100644 index 0000000000..3b7b344c6e --- /dev/null +++ b/detekt-baseline.xml @@ -0,0 +1,373 @@ + + + + + ChainWrapping:EditItemScreen.kt$+ + ChainWrapping:RootNavViewModel.kt$RootNavViewModel$&& + CyclomaticComplexMethod:BitwardenExportParser.kt$BitwardenExportParser$private fun ExportJsonData.ExportItem.toAuthenticatorItemEntity(): AuthenticatorItemEntity + EmptyFunctionBlock:MainViewModelTest.kt$MainViewModelTest${ } + LongMethod:AuthenticatorNavBarScreen.kt$@Composable private fun AuthenticatorBottomAppBar( navController: NavController, verificationCodesTabClickedAction: () -> Unit, settingsTabClickedAction: () -> Unit, modifier: Modifier = Modifier, ) + LongMethod:BitwardenExportParser.kt$BitwardenExportParser$private fun ExportJsonData.ExportItem.toAuthenticatorItemEntity(): AuthenticatorItemEntity + LongMethod:EditItemScreen.kt$@Composable fun EditItemContent( modifier: Modifier = Modifier, viewState: EditItemState.ViewState.Content, onIssuerNameTextChange: (String) -> Unit = {}, onUsernameTextChange: (String) -> Unit = {}, onToggleFavorite: (Boolean) -> Unit = {}, onTypeOptionClicked: (AuthenticatorItemType) -> Unit = {}, onTotpCodeTextChange: (String) -> Unit = {}, onAlgorithmOptionClicked: (AuthenticatorItemAlgorithm) -> Unit = {}, onRefreshPeriodOptionClicked: (AuthenticatorRefreshPeriodOption) -> Unit = {}, onNumberOfDigitsChanged: (Int) -> Unit = {}, onExpandAdvancedOptionsClicked: () -> Unit = {}, ) + LongMethod:EditItemScreen.kt$@Composable private fun AdvancedOptions( modifier: Modifier = Modifier, viewState: EditItemState.ViewState.Content, onExpandStateChange: () -> Unit, onAlgorithmOptionClicked: (AuthenticatorItemAlgorithm) -> Unit, onTypeOptionClicked: (AuthenticatorItemType) -> Unit, onRefreshPeriodOptionClicked: (AuthenticatorRefreshPeriodOption) -> Unit, onNumberOfDigitsChanged: (Int) -> Unit, ) + LongMethod:EditItemScreen.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable fun EditItemScreen( viewModel: EditItemViewModel = hiltViewModel(), onNavigateBack: () -> Unit = { }, ) + LongMethod:EditItemViewModel.kt$EditItemViewModel$private fun handleItemDataReceive(action: EditItemAction.Internal.EditItemDataReceive) + LongMethod:ExpandableFloatingActionButton.kt$@Composable fun <T : ExpandableFabOption> ExpandableFloatingActionButton( modifier: Modifier = Modifier, label: Text?, items: List<T>, expandableFabState: MutableState<ExpandableFabState> = rememberExpandableFabState(), expandableFabIcon: ExpandableFabIcon, onStateChange: (expandableFabState: ExpandableFabState) -> Unit = { }, ) + LongMethod:ExportScreen.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable fun ExportScreen( viewModel: ExportViewModel = hiltViewModel(), intentManager: IntentManager = LocalIntentManager.current, onNavigateBack: () -> Unit, ) + LongMethod:ImportingScreen.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable fun ImportingScreen( viewModel: ImportingViewModel = hiltViewModel(), intentManager: IntentManager = LocalIntentManager.current, onNavigateBack: () -> Unit, ) + LongMethod:ItemListingScreen.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable fun EmptyItemListingContent( modifier: Modifier = Modifier, appTheme: AppTheme, scrollBehavior: TopAppBarScrollBehavior = TopAppBarDefaults.pinnedScrollBehavior( rememberTopAppBarState() ), onAddCodeClick: () -> Unit, onScanQuCodeClick: () -> Unit, onEnterSetupKeyClick: () -> Unit, ) + LongMethod:ItemListingScreen.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable fun ItemListingScreen( viewModel: ItemListingViewModel = hiltViewModel(), intentManager: IntentManager = LocalIntentManager.current, permissionsManager: PermissionsManager = LocalPermissionsManager.current, onNavigateBack: () -> Unit, onNavigateToSearch: () -> Unit, onNavigateToQrCodeScanner: () -> Unit, onNavigateToManualKeyEntry: () -> Unit, onNavigateToEditItemScreen: (id: String) -> Unit, ) + LongMethod:ItemListingScreen.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable private fun ItemListingContent( state: ItemListingState.ViewState.Content, scrollBehavior: TopAppBarScrollBehavior, onNavigateToSearch: () -> Unit, onScanQrCodeClick: () -> Unit, onEnterSetupKeyClick: () -> Unit, onItemClick: (String) -> Unit, onEditItemClick: (String) -> Unit, onDeleteItemClick: (String) -> Unit, ) + LongMethod:ItemSearchScreen.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable fun ItemSearchScreen( viewModel: ItemSearchViewModel = hiltViewModel(), onNavigateBack: () -> Unit, ) + LongMethod:RootNavScreen.kt$@Composable fun RootNavScreen( viewModel: RootNavViewModel = hiltViewModel(), navController: NavHostController = rememberNavController(), onSplashScreenRemoved: () -> Unit = {}, onExitApplication: () -> Unit, ) + LongMethod:SettingsScreen.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable fun SettingsScreen( viewModel: SettingsViewModel = hiltViewModel(), biometricsManager: BiometricsManager = LocalBiometricsManager.current, intentManager: IntentManager = LocalIntentManager.current, onNavigateToTutorial: () -> Unit, onNavigateToExport: () -> Unit, onNavigateToImport: () -> Unit, ) + LongMethod:TutorialScreen.kt$@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class) @Composable fun TutorialScreen( viewModel: TutorialViewModel = hiltViewModel(), onTutorialFinished: () -> Unit, ) + LongMethod:UnlockScreen.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable fun UnlockScreen( viewModel: UnlockViewModel = hiltViewModel(), biometricsManager: BiometricsManager = LocalBiometricsManager.current, onUnlocked: () -> Unit, ) + LongParameterList:AuthenticatorNavBarNavigation.kt$( onNavigateBack: () -> Unit, onNavigateToSearch: () -> Unit, onNavigateToQrCodeScanner: () -> Unit, onNavigateToManualKeyEntry: () -> Unit, onNavigateToEditItem: (itemId: String) -> Unit, onNavigateToExport: () -> Unit, onNavigateToImport: () -> Unit, onNavigateToTutorial: () -> Unit, ) + LongParameterList:ItemListingGraphNavigation.kt$( navController: NavController, navigateBack: () -> Unit, navigateToSearch: () -> Unit, navigateToQrCodeScanner: () -> Unit, navigateToManualKeyEntry: () -> Unit, navigateToEditItem: (String) -> Unit, navigateToExport: () -> Unit, navigateToImport: () -> Unit, navigateToTutorial: () -> Unit, ) + MagicNumber:ItemListingViewModel.kt$ItemListingViewModel$10 + MagicNumber:ItemListingViewModel.kt$ItemListingViewModel$30 + MagicNumber:ItemListingViewModel.kt$ItemListingViewModel$5 + MagicNumber:ItemListingViewModel.kt$ItemListingViewModel$6 + MagicNumber:ItemSearchViewModel.kt$ItemSearchViewModel$8 + MagicNumber:SettingsDiskSourceImpl.kt$SettingsDiskSourceImpl$7 + MatchingDeclarationName:ExportVaultFormat.kt$ExportFormat + MaxLineLength:AegisExportParser.kt$AegisExportParser$private + MaxLineLength:BitwardenExportParser.kt$BitwardenExportParser$"${TotpCodeManager.TOTP_CODE_PREFIX}/$name?${TotpCodeManager.SECRET_PARAM}=$otpString" + MaxLineLength:FeatureFlag.kt$FeatureFlag$* + MaxLineLength:ImportingScreen.kt$launcher.launch(intentManager.createFileChooserIntent(event.importFileFormat.mimeType)) + MaxLineLength:ItemListingViewModel.kt$ItemListingViewModel$private + MaxLineLength:ItemSearchViewModel.kt$ItemSearchViewModel$private + MaxLineLength:TutorialScreen.kt$R.string.when_using_2_step_verification_youll_enter_your_username_and_password_and_a_code_generated_in_this_app + MaxLineLength:TwoFasExportParser.kt$TwoFasExportParser$private + MaximumLineLength:TutorialScreen.kt$ + NoBlankLineBeforeRbrace:AuthenticatorDatabase.kt$AuthenticatorDatabase$ + NoBlankLineBeforeRbrace:AuthenticatorDiskSource.kt$AuthenticatorDiskSource$ + NoBlankLineBeforeRbrace:AuthenticatorManagerModule.kt$AuthenticatorManagerModule$ + NoBlankLineBeforeRbrace:AuthenticatorRepositoryModule.kt$AuthenticatorRepositoryModule$ + NoBlankLineBeforeRbrace:AuthenticatorSdkModule.kt$AuthenticatorSdkModule$ + NoBlankLineBeforeRbrace:AuthenticatorSdkSourceImpl.kt$AuthenticatorSdkSourceImpl$ + NoBlankLineBeforeRbrace:ExportDataResult.kt$ExportDataResult$ + NoBlankLineBeforeRbrace:FeatureFlagDiskSourceTest.kt$FeatureFlagDiskSourceTest$ + NoBlankLineBeforeRbrace:ImportManager.kt$ImportManager$ + NoBlankLineBeforeRbrace:MainViewModel.kt$MainViewModel$ + NoBlankLineBeforeRbrace:QrCodeAnalyzerImpl.kt$QrCodeAnalyzerImpl$ + NoBlankLineBeforeRbrace:UnlockViewModel.kt$UnlockEvent$ + NoConsecutiveBlankLines:BaseViewModelTest.kt$ + NoConsecutiveBlankLines:FeatureFlag.kt$ + NoConsecutiveBlankLines:ZonedDateTimeSerializerTest.kt$ + SpacingAroundColon:AuthenticatorManagerModule.kt$AuthenticatorManagerModule$: + SpacingAroundCurly:ItemSearchScreen.kt${ + TooGenericExceptionCaught:AuthenticatorRepositoryImpl.kt$AuthenticatorRepositoryImpl$e: Exception + TooGenericExceptionCaught:ImportManagerImpl.kt$ImportManagerImpl$e: Throwable + TooGenericExceptionCaught:ItemListingViewModel.kt$ItemListingViewModel$e: Throwable + TooManyFunctions:AuthenticatorRepositoryImpl.kt$AuthenticatorRepositoryImpl : AuthenticatorRepository + TooManyFunctions:EditItemViewModel.kt$EditItemViewModel : BaseViewModel + TooManyFunctions:ItemListingViewModel.kt$ItemListingViewModel : BaseViewModel + TooManyFunctions:ItemSearchViewModel.kt$ItemSearchViewModel : BaseViewModel + TooManyFunctions:SettingsViewModel.kt$SettingsViewModel : BaseViewModel + TrailingCommaOnCallSite:AuthRepositoryModule.kt$AuthRepositoryModule$( authDiskSource = authDiskSource ) + TrailingCommaOnCallSite:AuthenticatorDatabase.kt$AuthenticatorDatabase$[ AuthenticatorItemEntity::class ] + TrailingCommaOnCallSite:AuthenticatorDiskModule.kt$AuthenticatorDiskModule$( context = app, klass = AuthenticatorDatabase::class.java, name = "authenticator_database" ) + TrailingCommaOnCallSite:AuthenticatorDiskSourceImpl.kt$AuthenticatorDiskSourceImpl$( forceItemsFlow, itemDao.getAllItems() ) + TrailingCommaOnCallSite:AuthenticatorManagerModule.kt$AuthenticatorManagerModule$( authenticatorSdkSource = authenticatorSdkSource, dispatcherManager = dispatcherManager, clock = clock ) + TrailingCommaOnCallSite:AuthenticatorNavBarNavigation.kt$( onNavigateBack = onNavigateBack, onNavigateToSearch = onNavigateToSearch, onNavigateToQrCodeScanner = onNavigateToQrCodeScanner, onNavigateToManualKeyEntry = onNavigateToManualKeyEntry, onNavigateToEditItem = onNavigateToEditItem, onNavigateToExport = onNavigateToExport, onNavigateToImport = onNavigateToImport, onNavigateToTutorial = onNavigateToTutorial ) + TrailingCommaOnCallSite:AuthenticatorNavBarScreen.kt$( isVisible = false, onClick = { // Do nothing }, modifier = Modifier .fillMaxWidth() .height(appBarHeightPx.toDp()) ) + TrailingCommaOnCallSite:AuthenticatorNavigation.kt$( navController = navController, navigateBack = onNavigateBack, navigateToSearch = { navController.navigateToSearch() }, navigateToQrCodeScanner = { navController.navigateToQrCodeScanScreen() }, navigateToManualKeyEntry = { navController.navigateToManualCodeEntryScreen() }, navigateToEditItem = { navController.navigateToEditItem(itemId = it) }, navigateToExport = { navController.navigateToExport() }, navigateToImport = { navController.navigateToImporting() }, navigateToTutorial = { navController.navigateToSettingsTutorial() } ) + TrailingCommaOnCallSite:AuthenticatorNavigation.kt$( onNavigateBack = onNavigateBack, onNavigateToSearch = { navController.navigateToSearch() }, onNavigateToQrCodeScanner = { navController.navigateToQrCodeScanScreen() }, onNavigateToManualKeyEntry = { navController.navigateToManualCodeEntryScreen() }, onNavigateToEditItem = { navController.navigateToEditItem(itemId = it) }, onNavigateToExport = { navController.navigateToExport() }, onNavigateToImport = { navController.navigateToImporting() }, onNavigateToTutorial = { navController.navigateToSettingsTutorial() } ) + TrailingCommaOnCallSite:AuthenticatorNavigation.kt$( startDestination = AUTHENTICATOR_NAV_BAR_ROUTE, route = AUTHENTICATOR_GRAPH_ROUTE ) + TrailingCommaOnCallSite:AuthenticatorRepositoryImpl.kt$AuthenticatorRepositoryImpl$( ExportJsonData( encrypted = false, items = authenticatorDiskSource .getItems() .firstOrNull() .orEmpty() .map { it.toExportJsonItem() }, ) ) + TrailingCommaOnCallSite:AuthenticatorRepositoryImpl.kt$AuthenticatorRepositoryImpl$( id = itemId, key = updateItemRequest.key, accountName = updateItemRequest.accountName, type = updateItemRequest.type, period = updateItemRequest.period, digits = updateItemRequest.digits, issuer = updateItemRequest.issuer, userId = null, favorite = updateItemRequest.favorite ) + TrailingCommaOnCallSite:AuthenticatorRepositoryImpl.kt$AuthenticatorRepositoryImpl$( importFileFormat = format, byteArray = it ) + TrailingCommaOnCallSite:AuthenticatorRepositoryImpl.kt$AuthenticatorRepositoryImpl$( onSuccess = { it }, onFailure = { ImportDataResult.Error() } ) + TrailingCommaOnCallSite:AuthenticatorSdkSourceImpl.kt$AuthenticatorSdkSourceImpl$( PasswordGeneratorRequest( lowercase = true, uppercase = true, numbers = true, special = true, length = 7.toUByte(), avoidAmbiguous = true, minLowercase = null, minUppercase = null, minNumber = null, minSpecial = null ) ) + TrailingCommaOnCallSite:AuthenticatorSdkSourceImpl.kt$AuthenticatorSdkSourceImpl$( lowercase = true, uppercase = true, numbers = true, special = true, length = 7.toUByte(), avoidAmbiguous = true, minLowercase = null, minUppercase = null, minNumber = null, minSpecial = null ) + TrailingCommaOnCallSite:BitwardenExportParser.kt$BitwardenExportParser$( items = exportData .items .filter { it.login?.totp != null } .toAuthenticatorItemEntities() ) + TrailingCommaOnCallSite:EditItemScreen.kt$( EditItemAction.AlgorithmOptionClick(it) ) + TrailingCommaOnCallSite:EditItemScreen.kt$( EditItemAction.ExpandAdvancedOptionsClick ) + TrailingCommaOnCallSite:EditItemScreen.kt$( EditItemAction.FavoriteToggleClick(it) ) + TrailingCommaOnCallSite:EditItemScreen.kt$( EditItemAction.IssuerNameTextChange(it) ) + TrailingCommaOnCallSite:EditItemScreen.kt$( EditItemAction.NumberOfDigitsOptionClick(it) ) + TrailingCommaOnCallSite:EditItemScreen.kt$( EditItemAction.RefreshPeriodOptionClick(it) ) + TrailingCommaOnCallSite:EditItemScreen.kt$( EditItemAction.TotpCodeTextChange(it) ) + TrailingCommaOnCallSite:EditItemScreen.kt$( EditItemAction.TypeOptionClick(it) ) + TrailingCommaOnCallSite:EditItemScreen.kt$( EditItemAction.UsernameTextChange(it) ) + TrailingCommaOnCallSite:EditItemScreen.kt$( context, event.message(resources), Toast.LENGTH_LONG ) + TrailingCommaOnCallSite:EditItemScreen.kt$( dialogState = state.dialog, onDismissRequest = { viewModel.trySendAction(EditItemAction.DismissDialog) } ) + TrailingCommaOnCallSite:EditItemScreen.kt$( iconData = IconData.Local( iconRes = if (viewState.isAdvancedOptionsExpanded) { R.drawable.ic_chevron_up } else { R.drawable.ic_chevron_down } ), contentDescription = stringResource( id = R.string.collapse_advanced_options ), tint = MaterialTheme.colorScheme.primary ) + TrailingCommaOnCallSite:EditItemScreen.kt$( iconRes = if (viewState.isAdvancedOptionsExpanded) { R.drawable.ic_chevron_up } else { R.drawable.ic_chevron_down } ) + TrailingCommaOnCallSite:EditItemScreen.kt$( id = R.string.collapse_advanced_options ) + TrailingCommaOnCallSite:EditItemScreen.kt$( id = R.string.refresh_period_seconds, it.seconds ) + TrailingCommaOnCallSite:EditItemScreen.kt$( indication = rememberRipple( bounded = true, color = MaterialTheme.colorScheme.primary, ), interactionSource = remember { MutableInteractionSource() } ) + TrailingCommaOnCallSite:EditItemScreen.kt$( modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp), viewState = viewState, onExpandStateChange = onExpandAdvancedOptionsClicked, onAlgorithmOptionClicked = onAlgorithmOptionClicked, onTypeOptionClicked = onTypeOptionClicked, onRefreshPeriodOptionClicked = onRefreshPeriodOptionClicked, onNumberOfDigitsChanged = onNumberOfDigitsChanged ) + TrailingCommaOnCallSite:EditItemScreen.kt$( modifier = Modifier .fillMaxWidth(), label = stringResource(id = R.string.refresh_period), options = refreshPeriodOptionsWithStrings.values.toImmutableList(), selectedOption = stringResource( id = R.string.refresh_period_seconds, viewState.itemData.refreshPeriod.seconds, ), onOptionSelected = remember(viewState) { { selectedOption -> val selectedOptionName = refreshPeriodOptionsWithStrings .entries .first { it.value == selectedOption } .key onRefreshPeriodOptionClicked(selectedOptionName) } } ) + TrailingCommaOnCallSite:EditItemScreen.kt$( modifier = Modifier .imePadding() .padding(innerPadding), viewState = viewState, onIssuerNameTextChange = remember(viewModel) { { viewModel.trySendAction( EditItemAction.IssuerNameTextChange(it) ) } }, onUsernameTextChange = remember(viewModel) { { viewModel.trySendAction( EditItemAction.UsernameTextChange(it) ) } }, onToggleFavorite = remember(viewModel) { { viewModel.trySendAction( EditItemAction.FavoriteToggleClick(it) ) } }, onTypeOptionClicked = remember(viewModel) { { viewModel.trySendAction( EditItemAction.TypeOptionClick(it) ) } }, onTotpCodeTextChange = remember(viewModel) { { viewModel.trySendAction( EditItemAction.TotpCodeTextChange(it) ) } }, onAlgorithmOptionClicked = remember(viewModel) { { viewModel.trySendAction( EditItemAction.AlgorithmOptionClick(it) ) } }, onRefreshPeriodOptionClicked = remember(viewModel) { { viewModel.trySendAction( EditItemAction.RefreshPeriodOptionClick(it) ) } }, onNumberOfDigitsChanged = remember(viewModel) { { viewModel.trySendAction( EditItemAction.NumberOfDigitsOptionClick(it) ) } }, onExpandAdvancedOptionsClicked = remember(viewModel) { { viewModel.trySendAction( EditItemAction.ExpandAdvancedOptionsClick ) } } ) + TrailingCommaOnCallSite:EditItemScreen.kt$( title = dialogState.title, message = dialogState.message ) + TrailingCommaOnCallSite:EditItemScreen.kt$( title = stringResource( id = R.string.edit_item, ), scrollBehavior = scrollBehavior, navigationIcon = painterResource(id = R.drawable.ic_close), navigationIconContentDescription = stringResource(id = R.string.close), onNavigationIconClick = remember(viewModel) { { viewModel.trySendAction(EditItemAction.CancelClick) } }, actions = { BitwardenTextButton( label = stringResource(id = R.string.save), onClick = remember(viewModel) { { viewModel.trySendAction(EditItemAction.SaveClick) } }, modifier = Modifier.semantics { testTag = "SaveButton" }, ) } ) + TrailingCommaOnCallSite:EditItemScreen.kt$( viewState = EditItemState.ViewState.Content( isAdvancedOptionsExpanded = false, itemData = EditItemData( refreshPeriod = AuthenticatorRefreshPeriodOption.THIRTY, totpCode = "123456", type = AuthenticatorItemType.TOTP, username = "account name", issuer = "issuer", algorithm = AuthenticatorItemAlgorithm.SHA1, digits = 6, favorite = false, ), minDigitsAllowed = 5, maxDigitsAllowed = 10, ) ) + TrailingCommaOnCallSite:EditItemScreen.kt$( viewState = EditItemState.ViewState.Content( isAdvancedOptionsExpanded = true, itemData = EditItemData( refreshPeriod = AuthenticatorRefreshPeriodOption.THIRTY, totpCode = "123456", type = AuthenticatorItemType.TOTP, username = "account name", issuer = "issuer", algorithm = AuthenticatorItemAlgorithm.SHA1, digits = 6, favorite = true, ), minDigitsAllowed = 5, maxDigitsAllowed = 10, ) ) + TrailingCommaOnCallSite:EditItemViewModel.kt$EditItemViewModel$( AuthenticatorItemEntity( id = state.itemId, key = content.itemData.totpCode.trim(), accountName = content.itemData.username?.trim(), type = content.itemData.type, algorithm = content.itemData.algorithm, period = content.itemData.refreshPeriod.seconds, digits = content.itemData.digits, issuer = content.itemData.issuer.trim(), favorite = content.itemData.favorite, ) ) + TrailingCommaOnCallSite:EditItemViewModel.kt$EditItemViewModel$( R.string.saving.asText() ) + TrailingCommaOnCallSite:EditItemViewModel.kt$EditItemViewModel$( algorithm = action.algorithmOption ) + TrailingCommaOnCallSite:EditItemViewModel.kt$EditItemViewModel$( dialog = EditItemState.DialogState.Generic( title = R.string.an_error_has_occurred.asText(), message = R.string.key_is_invalid.asText() ) ) + TrailingCommaOnCallSite:EditItemViewModel.kt$EditItemViewModel$( dialog = EditItemState.DialogState.Generic( title = R.string.an_error_has_occurred.asText(), message = R.string.validation_field_required.asText(R.string.key.asText()), ) ) + TrailingCommaOnCallSite:EditItemViewModel.kt$EditItemViewModel$( dialog = EditItemState.DialogState.Generic( title = R.string.an_error_has_occurred.asText(), message = R.string.validation_field_required.asText(R.string.name.asText()), ) ) + TrailingCommaOnCallSite:EditItemViewModel.kt$EditItemViewModel$( dialog = EditItemState.DialogState.Loading( R.string.saving.asText() ) ) + TrailingCommaOnCallSite:EditItemViewModel.kt$EditItemViewModel$( digits = action.digits ) + TrailingCommaOnCallSite:EditItemViewModel.kt$EditItemViewModel$( favorite = action.favorite ) + TrailingCommaOnCallSite:EditItemViewModel.kt$EditItemViewModel$( initialState = savedStateHandle[KEY_STATE] ?: EditItemState( itemId = EditItemArgs(savedStateHandle).itemId, viewState = EditItemState.ViewState.Loading, dialog = null, ) ) + TrailingCommaOnCallSite:EditItemViewModel.kt$EditItemViewModel$( isAdvancedOptionsExpanded = currentContent.isAdvancedOptionsExpanded.not() ) + TrailingCommaOnCallSite:EditItemViewModel.kt$EditItemViewModel$( issuer = action.issuerName ) + TrailingCommaOnCallSite:EditItemViewModel.kt$EditItemViewModel$( message = R.string.generic_error_message.asText() ) + TrailingCommaOnCallSite:EditItemViewModel.kt$EditItemViewModel$( refreshPeriod = action.period ) + TrailingCommaOnCallSite:EditItemViewModel.kt$EditItemViewModel$( title = R.string.an_error_has_occurred.asText(), message = R.string.key_is_invalid.asText() ) + TrailingCommaOnCallSite:EditItemViewModel.kt$EditItemViewModel$( totpCode = action.totpCode ) + TrailingCommaOnCallSite:EditItemViewModel.kt$EditItemViewModel$( type = action.typeOption ) + TrailingCommaOnCallSite:EditItemViewModel.kt$EditItemViewModel$( username = action.username ) + TrailingCommaOnCallSite:EditItemViewModel.kt$EditItemViewModel$( viewState = EditItemState.ViewState.Loading ) + TrailingCommaOnCallSite:ExpandableFloatingActionButton.kt$( onFabOptionClick = { expandableFabState.value = expandableFabState.value.toggleValue() onStateChange(expandableFabState.value) expandableFabOption.onFabOptionClick() }, expandableFabOption = expandableFabOption ) + TrailingCommaOnCallSite:ExportScreen.kt$( modifier = Modifier .padding(paddingValues) .fillMaxSize(), state = state, onExportFormatOptionSelected = remember(viewModel) { { viewModel.trySendAction(ExportAction.ExportFormatOptionSelect(it)) } }, onExportClick = { shouldShowConfirmationPrompt = true } ) + TrailingCommaOnCallSite:ExportScreen.kt$( modifier = modifier .imePadding() .verticalScroll(rememberScrollState()) ) + TrailingCommaOnCallSite:ExportScreen.kt$( text = dialog.message ) + TrailingCommaOnCallSite:ExportScreen.kt$( visibilityState = BasicDialogState.Shown( title = dialog.title, message = dialog.message, ), onDismissRequest = remember(viewModel) { { viewModel.trySendAction(ExportAction.DialogDismiss) } } ) + TrailingCommaOnCallSite:ExportScreen.kt$( visibilityState = LoadingDialogState.Shown( text = dialog.message ) ) + TrailingCommaOnCallSite:ExportViewModel.kt$ExportViewModel$( ExportAction.Internal.SaveExportDataToUriResultReceive( result = result ) ) + TrailingCommaOnCallSite:ExportViewModel.kt$ExportViewModel$( initialState = ExportState(dialogState = null, exportFormat = ExportFormat.JSON) ) + TrailingCommaOnCallSite:ExportViewModel.kt$ExportViewModel$( result = result ) + TrailingCommaOnCallSite:FakeFeatureFlagRepository.kt$( LocalFeatureFlag.BitwardenAuthenticationEnabled.name to JsonPrimitive(LocalFeatureFlag.BitwardenAuthenticationEnabled.defaultValue) ) + TrailingCommaOnCallSite:FakeFeatureFlagRepository.kt$( featureFlags = mapOf( LocalFeatureFlag.BitwardenAuthenticationEnabled.name to JsonPrimitive(LocalFeatureFlag.BitwardenAuthenticationEnabled.defaultValue) ) ) + TrailingCommaOnCallSite:FeatureFlagDiskSourceTest.kt$( LocalFeatureFlag.BitwardenAuthenticationEnabled.name to JsonPrimitive(true) ) + TrailingCommaOnCallSite:FeatureFlagDiskSourceTest.kt$( featureFlags = mapOf( LocalFeatureFlag.BitwardenAuthenticationEnabled.name to JsonPrimitive(true) ) ) + TrailingCommaOnCallSite:FeatureFlagDiskSourceTest.kt$FeatureFlagDiskSourceTest$( FEATURE_FLAGS_CONFIGURATION_JSON ) + TrailingCommaOnCallSite:FeatureFlagDiskSourceTest.kt$FeatureFlagDiskSourceTest$( fakeSharedPreferences.getString(featureFlagsConfigKey, null)!! ) + TrailingCommaOnCallSite:FeatureFlagManagerTest.kt$( mapOf( LocalFeatureFlag.BitwardenAuthenticationEnabled.name to JsonPrimitive(true), ) ) + TrailingCommaOnCallSite:FeatureFlagManagerTest.kt$FeatureFlagManagerTest$( featureFlags = emptyMap() ) + TrailingCommaOnCallSite:FeatureFlagRepositoryImpl.kt$FeatureFlagRepositoryImpl$( LocalFeatureFlag.BitwardenAuthenticationEnabled.defaultValue ) + TrailingCommaOnCallSite:FeatureFlagRepositoryImpl.kt$FeatureFlagRepositoryImpl$( LocalFeatureFlag.BitwardenAuthenticationEnabled.name to JsonPrimitive( LocalFeatureFlag.BitwardenAuthenticationEnabled.defaultValue ) ) + TrailingCommaOnCallSite:FeatureFlagRepositoryImpl.kt$FeatureFlagRepositoryImpl$( mapOf( LocalFeatureFlag.BitwardenAuthenticationEnabled.name to JsonPrimitive( LocalFeatureFlag.BitwardenAuthenticationEnabled.defaultValue ) ) ) + TrailingCommaOnCallSite:FeatureFlagRepositoryTest.kt$( featureFlags = mapOf( LocalFeatureFlag.BitwardenAuthenticationEnabled.name to JsonPrimitive(LocalFeatureFlag.BitwardenAuthenticationEnabled.defaultValue), ) ) + TrailingCommaOnCallSite:FeatureFlagRepositoryTest.kt$FeatureFlagRepositoryTest$( FEATURE_FLAGS_CONFIG, fakeFeatureFlagDiskSource.featureFlagsConfiguration ) + TrailingCommaOnCallSite:ImportingScreen.kt$( modifier = modifier .imePadding() .verticalScroll(rememberScrollState()) ) + TrailingCommaOnCallSite:ImportingScreen.kt$( text = dialog.message ) + TrailingCommaOnCallSite:ImportingScreen.kt$( visibilityState = BasicDialogState.Shown( title = dialog.title, message = dialog.message, ), onDismissRequest = remember(viewModel) { { viewModel.trySendAction(ImportAction.DialogDismiss) } } ) + TrailingCommaOnCallSite:ImportingScreen.kt$( visibilityState = LoadingDialogState.Shown( text = dialog.message ) ) + TrailingCommaOnCallSite:ImportingViewModel.kt$ImportingViewModel$( ImportEvent.ShowToast( message = R.string.import_success.asText(), ) ) + TrailingCommaOnCallSite:ImportingViewModel.kt$ImportingViewModel$( dialogState = ImportState.DialogState.Error( title = R.string.an_error_has_occurred.asText(), message = result.message ?: R.string.import_vault_failure.asText(), ) ) + TrailingCommaOnCallSite:ImportingViewModel.kt$ImportingViewModel$( initialState = ImportState(importFileFormat = ImportFileFormat.BITWARDEN_JSON) ) + TrailingCommaOnCallSite:ItemListingExpandableFabAction.kt$ItemListingExpandableFabAction.EnterSetupKey$( label, icon, onEnterSetupKeyClick ) + TrailingCommaOnCallSite:ItemListingExpandableFabAction.kt$ItemListingExpandableFabAction.ScanQrCode$( label, icon, onScanQrCodeClick ) + TrailingCommaOnCallSite:ItemListingGraphNavigation.kt$( navController = navController, onNavigateToExport = navigateToExport, onNavigateToImport = navigateToImport, onNavigateToTutorial = navigateToTutorial ) + TrailingCommaOnCallSite:ItemListingGraphNavigation.kt$( onNavigateBack = { navController.popBackStack() } ) + TrailingCommaOnCallSite:ItemListingGraphNavigation.kt$( onNavigateBack = { navController.popBackStack() }, onNavigateToQrCodeScreen = { navController.popBackStack() navController.navigateToQrCodeScanScreen() } ) + TrailingCommaOnCallSite:ItemListingGraphNavigation.kt$( route = ITEM_LISTING_GRAPH_ROUTE, navOptions = navOptions ) + TrailingCommaOnCallSite:ItemListingScreen.kt$( ItemListingAction.DeleteItemClick(it) ) + TrailingCommaOnCallSite:ItemListingScreen.kt$( ItemListingAction.EditItemClick(it) ) + TrailingCommaOnCallSite:ItemListingScreen.kt$( ItemListingAction.ItemClick(it) ) + TrailingCommaOnCallSite:ItemListingScreen.kt$( ItemListingAction.SearchClick ) + TrailingCommaOnCallSite:ItemListingScreen.kt$( ItemListingExpandableFabAction.ScanQrCode( label = R.string.scan_a_qr_code.asText(), icon = IconResource( iconPainter = painterResource(id = R.drawable.ic_camera), contentDescription = stringResource(id = R.string.scan_a_qr_code), testTag = "ScanQRCodeButton", ), onScanQrCodeClick = onScanQrCodeClick, ), ItemListingExpandableFabAction.EnterSetupKey( label = R.string.enter_key_manually.asText(), icon = IconResource( iconPainter = painterResource(id = R.drawable.ic_keyboard_24px), contentDescription = stringResource(id = R.string.enter_key_manually), testTag = "EnterSetupKeyButton", ), onEnterSetupKeyClick = onEnterSetupKeyClick ) ) + TrailingCommaOnCallSite:ItemListingScreen.kt$( ItemListingExpandableFabAction.ScanQrCode( label = R.string.scan_a_qr_code.asText(), icon = IconResource( iconPainter = painterResource(id = R.drawable.ic_camera), contentDescription = stringResource(id = R.string.scan_a_qr_code), testTag = "ScanQRCodeButton", ), onScanQrCodeClick = onScanQuCodeClick ), ItemListingExpandableFabAction.EnterSetupKey( label = R.string.enter_key_manually.asText(), icon = IconResource( iconPainter = painterResource(id = R.drawable.ic_keyboard_24px), contentDescription = stringResource(id = R.string.enter_key_manually), testTag = "EnterSetupKeyButton", ), onEnterSetupKeyClick = onEnterSetupKeyClick, ) ) + TrailingCommaOnCallSite:ItemListingScreen.kt$( appTheme = state.appTheme, scrollBehavior = scrollBehavior, onAddCodeClick = remember(viewModel) { { launcher.launch(Manifest.permission.CAMERA) } }, onScanQuCodeClick = remember(viewModel) { { launcher.launch(Manifest.permission.CAMERA) } }, onEnterSetupKeyClick = remember(viewModel) { { viewModel.trySendAction(ItemListingAction.EnterSetupKeyClick) } } ) + TrailingCommaOnCallSite:ItemListingScreen.kt$( context, event.message(context.resources), Toast.LENGTH_LONG ) + TrailingCommaOnCallSite:ItemListingScreen.kt$( currentState, scrollBehavior, onNavigateToSearch = remember(viewModel) { { viewModel.trySendAction( ItemListingAction.SearchClick ) } }, onScanQrCodeClick = remember(viewModel) { { launcher.launch(Manifest.permission.CAMERA) } }, onEnterSetupKeyClick = remember(viewModel) { { viewModel.trySendAction(ItemListingAction.EnterSetupKeyClick) } }, onItemClick = remember(viewModel) { { viewModel.trySendAction( ItemListingAction.ItemClick(it) ) } }, onEditItemClick = remember(viewModel) { { viewModel.trySendAction( ItemListingAction.EditItemClick(it) ) } }, onDeleteItemClick = remember(viewModel) { { viewModel.trySendAction( ItemListingAction.DeleteItemClick(it) ) } } ) + TrailingCommaOnCallSite:ItemListingScreen.kt$( id = when (appTheme) { AppTheme.DARK -> R.drawable.ic_empty_vault_dark AppTheme.LIGHT -> R.drawable.ic_empty_vault_light AppTheme.DEFAULT -> R.drawable.ic_empty_vault } ) + TrailingCommaOnCallSite:ItemListingScreen.kt$( label = R.string.enter_key_manually.asText(), icon = IconResource( iconPainter = painterResource(id = R.drawable.ic_keyboard_24px), contentDescription = stringResource(id = R.string.enter_key_manually), testTag = "EnterSetupKeyButton", ), onEnterSetupKeyClick = onEnterSetupKeyClick ) + TrailingCommaOnCallSite:ItemListingScreen.kt$( label = R.string.scan_a_qr_code.asText(), icon = IconResource( iconPainter = painterResource(id = R.drawable.ic_camera), contentDescription = stringResource(id = R.string.scan_a_qr_code), testTag = "ScanQRCodeButton", ), onScanQrCodeClick = onScanQuCodeClick ) + TrailingCommaOnCallSite:ItemListingScreen.kt$( rememberTopAppBarState() ) + TrailingCommaOnCallSite:ItemListingScreen.kt$( title = stringResource(id = R.string.delete), message = dialog.message(), confirmButtonText = stringResource(id = R.string.ok), dismissButtonText = stringResource(id = R.string.cancel), onConfirmClick = { onConfirmDeleteClick(dialog.itemId) }, onDismissClick = onDismissRequest, onDismissRequest = onDismissRequest ) + TrailingCommaOnCallSite:ItemListingScreen.kt$( title = stringResource(id = R.string.verification_codes), scrollBehavior = scrollBehavior, actions = { BitwardenSearchActionItem( contentDescription = stringResource(id = R.string.search_codes), onClick = onNavigateToSearch, ) } ) + TrailingCommaOnCallSite:ItemListingScreen.kt$( title = stringResource(id = R.string.verification_codes), scrollBehavior = scrollBehavior, navigationIcon = null, actions = { } ) + TrailingCommaOnCallSite:ItemListingViewModel.kt$ItemListingViewModel$( ItemListingAction.Internal.DeleteItemReceive( authenticatorRepository.hardDeleteItem(action.itemId) ) ) + TrailingCommaOnCallSite:ItemListingViewModel.kt$ItemListingViewModel$( ItemListingEvent.ShowToast( message = R.string.value_has_been_copied.asText(action.authCode) ) ) + TrailingCommaOnCallSite:ItemListingViewModel.kt$ItemListingViewModel$( authenticatorData = action.data, clearDialogState = false ) + TrailingCommaOnCallSite:ItemListingViewModel.kt$ItemListingViewModel$( authenticatorData = authenticatorData.data, clearDialogState = false ) + TrailingCommaOnCallSite:ItemListingViewModel.kt$ItemListingViewModel$( authenticatorData = authenticatorData.data, clearDialogState = true ) + TrailingCommaOnCallSite:ItemListingViewModel.kt$ItemListingViewModel$( authenticatorData = state.data, clearDialogState = true ) + TrailingCommaOnCallSite:ItemListingViewModel.kt$ItemListingViewModel$( authenticatorRepository.hardDeleteItem(action.itemId) ) + TrailingCommaOnCallSite:ItemListingViewModel.kt$ItemListingViewModel$( byteArray = otpParam.secret.toByteArray() ) + TrailingCommaOnCallSite:ItemListingViewModel.kt$ItemListingViewModel$( dialog = ItemListingState.DialogState.DeleteConfirmationPrompt( message = R.string.do_you_really_want_to_permanently_delete_cipher.asText(), itemId = action.itemId, ) ) + TrailingCommaOnCallSite:ItemListingViewModel.kt$ItemListingViewModel$( dialog = ItemListingState.DialogState.Error( title = R.string.an_error_has_occurred.asText(), message = R.string.authenticator_key_read_error.asText(), ) ) + TrailingCommaOnCallSite:ItemListingViewModel.kt$ItemListingViewModel$( dialog = ItemListingState.DialogState.Error( title = R.string.an_error_has_occurred.asText(), message = R.string.generic_error_message.asText(), ) ) + TrailingCommaOnCallSite:ItemListingViewModel.kt$ItemListingViewModel$( initialState = ItemListingState( settingsRepository.appTheme, settingsRepository.authenticatorAlertThresholdSeconds, viewState = ItemListingState.ViewState.Loading, dialog = null ) ) + TrailingCommaOnCallSite:ItemListingViewModel.kt$ItemListingViewModel$( message = R.string.internet_connection_required_title .asText() .concat(R.string.internet_connection_required_message.asText()) ) + TrailingCommaOnCallSite:ItemListingViewModel.kt$ItemListingViewModel$( message = R.string.value_has_been_copied.asText(action.authCode) ) + TrailingCommaOnCallSite:ItemListingViewModel.kt$ItemListingViewModel$( settingsRepository.appTheme, settingsRepository.authenticatorAlertThresholdSeconds, viewState = ItemListingState.ViewState.Loading, dialog = null ) + TrailingCommaOnCallSite:ItemListingViewModel.kt$ItemListingViewModel$( viewState = ItemListingState.ViewState.Loading ) + TrailingCommaOnCallSite:ItemSearchNavigation.kt$( onNavigateBack = onNavigateBack ) + TrailingCommaOnCallSite:ItemSearchScreen.kt$( viewState = viewState, modifier = innerModifier ) + TrailingCommaOnCallSite:ItemSearchViewModel.kt$ItemSearchViewModel$( authenticatorData = authenticatorData, clearDialogState = false ) + TrailingCommaOnCallSite:ItemSearchViewModel.kt$ItemSearchViewModel$( initialState = ItemSearchState( searchTerm = "", viewState = ItemSearchState.ViewState.Loading, dialogState = null, ) ) + TrailingCommaOnCallSite:ItemSearchViewModel.kt$ItemSearchViewModel$( message = R.string.there_are_no_items_that_match_the_search.asText() ) + TrailingCommaOnCallSite:ItemSearchViewModel.kt$ItemSearchViewModel$( searchTerm = currentState.searchTerm, viewState = authenticatorData .filterAndOrganize(state.searchTerm) .toViewState(searchTerm = state.searchTerm), dialogState = currentState.dialogState.takeUnless { clearDialogState } ) + TrailingCommaOnCallSite:MainActivity.kt$MainActivity$( MainAction.ReceiveFirstIntent( intent = intent ) ) + TrailingCommaOnCallSite:MainActivity.kt$MainActivity$( MainAction.ReceiveNewIntent(intent = intent) ) + TrailingCommaOnCallSite:MainActivity.kt$MainActivity$( intent = intent ) + TrailingCommaOnCallSite:MainActivity.kt$MainActivity$( onSplashScreenRemoved = { shouldShowSplashScreen = false }, onExitApplication = { finishAffinity() } ) + TrailingCommaOnCallSite:MainActivity.kt$MainActivity$( theme = state.theme ) + TrailingCommaOnCallSite:MainViewModel.kt$MainViewModel$( MainState( theme = settingsRepository.appTheme ) ) + TrailingCommaOnCallSite:MainViewModel.kt$MainViewModel$( theme = settingsRepository.appTheme ) + TrailingCommaOnCallSite:ManualCodeEntryScreen.kt$( dialog.message ) + TrailingCommaOnCallSite:ManualCodeEntryScreen.kt$( title = dialog.title, message = dialog.message ) + TrailingCommaOnCallSite:ManualCodeEntryScreen.kt$( visibilityState = BasicDialogState.Shown( title = dialog.title, message = dialog.message ), onDismissRequest = remember(state) { { viewModel.trySendAction(ManualCodeEntryAction.DismissDialog) } } ) + TrailingCommaOnCallSite:ManualCodeEntryScreen.kt$( visibilityState = LoadingDialogState.Shown( dialog.message ) ) + TrailingCommaOnCallSite:ManualCodeEntryViewModel.kt$ManualCodeEntryViewModel$( AuthenticatorItemEntity( id = UUID.randomUUID().toString(), key = state.code, issuer = state.issuer, accountName = "", userId = null, type = if (state.code.startsWith("steam://")) { AuthenticatorItemType.STEAM } else { AuthenticatorItemType.TOTP }, favorite = false, ) ) + TrailingCommaOnCallSite:ManualCodeEntryViewModel.kt$ManualCodeEntryViewModel$( dialog = ManualCodeEntryState.DialogState.Error( message = R.string.key_is_invalid.asText() ) ) + TrailingCommaOnCallSite:ManualCodeEntryViewModel.kt$ManualCodeEntryViewModel$( dialog = ManualCodeEntryState.DialogState.Error( message = R.string.key_is_required.asText() ) ) + TrailingCommaOnCallSite:ManualCodeEntryViewModel.kt$ManualCodeEntryViewModel$( dialog = ManualCodeEntryState.DialogState.Error( message = R.string.name_is_required.asText() ) ) + TrailingCommaOnCallSite:ManualCodeEntryViewModel.kt$ManualCodeEntryViewModel$( dialog = ManualCodeEntryState.DialogState.Error( title = R.string.an_error_has_occurred.asText(), message = R.string.generic_error_message.asText(), ) ) + TrailingCommaOnCallSite:ManualCodeEntryViewModel.kt$ManualCodeEntryViewModel$( message = R.string.key_is_invalid.asText() ) + TrailingCommaOnCallSite:ManualCodeEntryViewModel.kt$ManualCodeEntryViewModel$( message = R.string.key_is_required.asText() ) + TrailingCommaOnCallSite:ManualCodeEntryViewModel.kt$ManualCodeEntryViewModel$( message = R.string.name_is_required.asText() ) + TrailingCommaOnCallSite:QrCodeAnalyzerImpl.kt$QrCodeAnalyzerImpl$( DecodeHintType.POSSIBLE_FORMATS to arrayListOf(BarcodeFormat.QR_CODE), DecodeHintType.ALSO_INVERTED to true ) + TrailingCommaOnCallSite:RootNavScreen.kt$( onUnlocked = { viewModel.trySendAction(RootNavAction.Internal.AppUnlocked) } ) + TrailingCommaOnCallSite:RootNavViewModel.kt$RootNavViewModel$( initialState = RootNavState( hasSeenWelcomeGuide = settingsRepository.hasSeenWelcomeTutorial, navState = RootNavState.NavState.Splash, ) ) + TrailingCommaOnCallSite:SearchHandlers.kt$SearchHandlers.Companion$( onBackClick = { viewModel.trySendAction(ItemSearchAction.BackClick) }, onDismissRequest = { viewModel.trySendAction(ItemSearchAction.DismissDialogClick) }, onItemClick = { viewModel.trySendAction(ItemSearchAction.ItemClick(it)) }, onSearchTermChange = { viewModel.trySendAction(ItemSearchAction.SearchTermChange(it)) } ) + TrailingCommaOnCallSite:SettingsDiskSourceImpl.kt$SettingsDiskSourceImpl$( ALERT_THRESHOLD_SECONDS_KEY, thresholdSeconds ) + TrailingCommaOnCallSite:SettingsNavigation.kt$( onNavigateBack = { navController.popBackStack() } ) + TrailingCommaOnCallSite:SettingsNavigation.kt$( onTutorialFinished = { navController.popBackStack() } ) + TrailingCommaOnCallSite:SettingsNavigation.kt$( route = SETTINGS_ROUTE ) + TrailingCommaOnCallSite:SettingsNavigation.kt$( startDestination = SETTINGS_ROUTE, route = SETTINGS_GRAPH_ROUTE ) + TrailingCommaOnCallSite:SettingsScreen.kt$( SettingsAction.SecurityClick.UnlockWithBiometricToggle(it) ) + TrailingCommaOnCallSite:SettingsScreen.kt$( id = R.drawable.ic_navigate_next ) + TrailingCommaOnCallSite:SettingsScreen.kt$( modifier = Modifier .padding(innerPadding) .fillMaxSize() .verticalScroll(state = rememberScrollState()) ) + TrailingCommaOnCallSite:SettingsScreen.kt$( modifier = Modifier.padding(horizontal = 16.dp), label = stringResource(id = R.string.appearance) ) + TrailingCommaOnCallSite:SettingsScreen.kt$( modifier = Modifier.padding(horizontal = 16.dp), label = stringResource(id = R.string.data) ) + TrailingCommaOnCallSite:SettingsScreen.kt$( modifier = Modifier.padding(horizontal = 16.dp), label = stringResource(id = R.string.help) ) + TrailingCommaOnCallSite:SettingsScreen.kt$( modifier = Modifier.padding(horizontal = 16.dp), label = stringResource(id = R.string.security) ) + TrailingCommaOnCallSite:SettingsScreen.kt$( modifier = modifier, label = stringResource(id = R.string.about) ) + TrailingCommaOnCallSite:SettingsScreen.kt$( onExportClick = remember(viewModel) { { viewModel.trySendAction(SettingsAction.DataClick.ExportClick) } }, onImportClick = remember(viewModel) { { viewModel.trySendAction(SettingsAction.DataClick.ImportClick) } }, onBackupClick = remember(viewModel) { { viewModel.trySendAction(SettingsAction.DataClick.BackupClick) } } ) + TrailingCommaOnCallSite:SettingsScreen.kt$( state = state, biometricsManager = biometricsManager, onBiometricToggle = remember(viewModel) { { viewModel.trySendAction( SettingsAction.SecurityClick.UnlockWithBiometricToggle(it) ) } } ) + TrailingCommaOnCallSite:SettingsScreen.kt$( text = stringResource(id = R.string.export), onClick = onExportClick, modifier = modifier, withDivider = true, content = { Icon( modifier = Modifier .mirrorIfRtl() .size(24.dp), painter = painterResource(id = R.drawable.ic_navigate_next), contentDescription = null, tint = MaterialTheme.colorScheme.onSurface, ) } ) + TrailingCommaOnCallSite:SettingsScreen.kt$( text = stringResource(id = R.string.import_vault), onClick = onImportClick, modifier = modifier, withDivider = true, content = { Icon( modifier = Modifier .mirrorIfRtl() .size(24.dp), painter = painterResource(id = R.drawable.ic_navigate_next), contentDescription = null, tint = MaterialTheme.colorScheme.onSurface, ) } ) + TrailingCommaOnCallSite:SettingsScreen.kt$( title = stringResource(id = R.string.settings), scrollBehavior = scrollBehavior ) + TrailingCommaOnCallSite:SettingsViewModel.kt$SettingsViewModel$( appearance = it.appearance.copy(language = language) ) + TrailingCommaOnCallSite:SettingsViewModel.kt$SettingsViewModel$( appearance = it.appearance.copy(theme = theme) ) + TrailingCommaOnCallSite:SettingsViewModel.kt$SettingsViewModel$( clock, settingsRepository.appLanguage, settingsRepository.appTheme, settingsRepository.isUnlockWithBiometricsEnabled, settingsRepository.isCrashLoggingEnabled ) + TrailingCommaOnCallSite:SettingsViewModel.kt$SettingsViewModel$( initialState = savedStateHandle[KEY_STATE] ?: createInitialState( clock, settingsRepository.appLanguage, settingsRepository.appTheme, settingsRepository.isUnlockWithBiometricsEnabled, settingsRepository.isCrashLoggingEnabled ) ) + TrailingCommaOnCallSite:SettingsViewModel.kt$SettingsViewModel.Companion$( appearance = SettingsState.Appearance( language = appLanguage, theme = appTheme, ), isUnlockWithBiometricsEnabled = unlockWithBiometricsEnabled, isSubmitCrashLogsEnabled = isSubmitCrashLogsEnabled, dialog = null, version = R.string.version .asText() .concat(": ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})".asText()), copyrightInfo = copyrightInfo ) + TrailingCommaOnCallSite:TutorialScreen.kt$( R.string.scan_the_qr_code_in_your_2_step_verification_settings_for_any_account ) + TrailingCommaOnCallSite:TutorialScreen.kt$( R.string.use_your_device_camera_to_scan_codes ) + TrailingCommaOnCallSite:TutorialScreen.kt$( R.string.when_using_2_step_verification_youll_enter_your_username_and_password_and_a_code_generated_in_this_app ) + TrailingCommaOnCallSite:TutorialScreen.kt$( TutorialAction.ContinueClick ) + TrailingCommaOnCallSite:TutorialScreen.kt$( TutorialAction.TutorialPageChange(pagerState.targetPage) ) + TrailingCommaOnCallSite:TutorialScreen.kt$( modifier = Modifier .height(50.dp) .fillMaxWidth(), horizontalArrangement = Arrangement.Center ) + TrailingCommaOnCallSite:TutorialScreen.kt$( modifier = Modifier .padding(8.dp) .background(color, CircleShape) .size(10.dp) ) + TrailingCommaOnCallSite:TutorialScreen.kt$( modifier = Modifier .padding(innerPadding), verticalArrangement = Arrangement.Center ) + TrailingCommaOnCallSite:TutorialScreen.kt$( onTutorialFinished = {} ) + TrailingCommaOnCallSite:TutorialScreen.kt$( painter = painterResource(id = R.drawable.ic_tutorial_2fa), contentDescription = stringResource(id = R.string.unique_codes) ) + TrailingCommaOnCallSite:TutorialViewModel.kt$TutorialState.QrScannerSlide$( continueButtonText = R.string.continue_button.asText(), isLastPage = false ) + TrailingCommaOnCallSite:TutorialViewModel.kt$TutorialState.UniqueCodesSlide$( continueButtonText = R.string.get_started.asText(), isLastPage = true ) + TrailingCommaOnCallSite:TutorialViewModel.kt$TutorialViewModel$( initialState = TutorialState.IntroSlide ) + TrailingCommaOnCallSite:TwoFasExportParser.kt$TwoFasExportParser$( items = exportData.services.toAuthenticatorItemEntities() ) + TrailingCommaOnCallSite:UnlockScreen.kt$( modifier = Modifier .padding(innerPadding) .fillMaxSize(), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) + TrailingCommaOnCallSite:UnlockScreen.kt$( title = R.string.an_error_has_occurred.asText(), message = dialog.message ) + TrailingCommaOnCallSite:UnlockScreen.kt$( visibilityState = LoadingDialogState.Shown(R.string.loading.asText()) ) + TrailingCommaOnCallSite:UnlockViewModel.kt$UnlockViewModel$( dialog = UnlockState.Dialog.Error( message = R.string.too_many_failed_biometric_attempts.asText(), ) ) + TrailingCommaOnCallSite:UnlockViewModel.kt$UnlockViewModel$( initialState = savedStateHandle[KEY_STATE] ?: run { UnlockState( isBiometricsEnabled = settingsRepository.isUnlockWithBiometricsEnabled, isBiometricsValid = biometricsEncryptionManager.isBiometricIntegrityValid(), dialog = null, ) } ) + TrailingCommaOnCallSite:VaultVerificationCodeItem.kt$( interactionSource = remember { MutableInteractionSource() }, indication = rememberRipple(color = MaterialTheme.colorScheme.primary), onClick = onItemClick, onLongClick = { shouldShowDropdownMenu = true } ) + TrailingCommaOnCallSite:VaultVerificationCodeItem.kt$( painter = painterResource(id = R.drawable.ic_edit_item), contentDescription = stringResource(R.string.edit_item) ) + TrailingCommaOnCallSite:VaultVerificationCodeItem.kt$( startIcon = IconData.Local(R.drawable.ic_login_item), issuer = "Sample Label", supportingLabel = "Supporting Label", authCode = "1234567890".chunked(3).joinToString(" "), timeLeftSeconds = 15, periodSeconds = 30, onCopyClick = {}, onItemClick = {}, modifier = Modifier.padding(horizontal = 16.dp), alertThresholdSeconds = 7 ) + TrailingCommaOnCallSite:VaultVerificationCodeItem.kt$( text = { Text(text = stringResource(id = R.string.delete_item)) }, onClick = { shouldShowDropdownMenu = false onDeleteItemClick() }, leadingIcon = { Icon( painter = painterResource(id = R.drawable.ic_delete_item), contentDescription = stringResource(id = R.string.delete_item), ) } ) + TrailingCommaOnCallSite:VaultVerificationCodeItem.kt$( text = { Text(text = stringResource(id = R.string.edit_item)) }, onClick = { shouldShowDropdownMenu = false onEditItemClick() }, leadingIcon = { Icon( painter = painterResource(id = R.drawable.ic_edit_item), contentDescription = stringResource(R.string.edit_item) ) } ) + TrailingCommaOnCallSite:VaultVerificationCodeItem.kt$( timeLeftSeconds = timeLeftSeconds, periodSeconds = periodSeconds, alertThresholdSeconds = alertThresholdSeconds ) + TrailingCommaOnCallSite:VaultVerificationCodeItem.kt$( vertical = 8.dp, horizontal = 16.dp ) + TrailingCommaOnDeclarationSite:ItemSearchViewModel.kt$SortPriority + UndocumentedPublicClass:AegisExportParser.kt$AegisExportParser : ExportParser + UndocumentedPublicClass:AegisJsonExport.kt$AegisJsonExport + UndocumentedPublicClass:AegisJsonExport.kt$AegisJsonExport$Database + UndocumentedPublicClass:AegisJsonExport.kt$AegisJsonExport.Database$Entry + UndocumentedPublicClass:AegisJsonExport.kt$AegisJsonExport.Database$Group + UndocumentedPublicClass:AegisJsonExport.kt$AegisJsonExport.Database.Entry$Info + UndocumentedPublicClass:AuthRepositoryImpl.kt$AuthRepositoryImpl : AuthRepository + UndocumentedPublicClass:AuthenticatorApplication.kt$AuthenticatorApplication : Application + UndocumentedPublicClass:AuthenticatorDiskSourceImpl.kt$AuthenticatorDiskSourceImpl : AuthenticatorDiskSource + UndocumentedPublicClass:AuthenticatorRepositoryImpl.kt$AuthenticatorRepositoryImpl : AuthenticatorRepository + UndocumentedPublicClass:AuthenticatorSdkSourceImpl.kt$AuthenticatorSdkSourceImpl : AuthenticatorSdkSource + UndocumentedPublicClass:BitwardenExportParser.kt$BitwardenExportParser : ExportParser + UndocumentedPublicClass:EditItemViewModel.kt$EditItemAction$ExpandAdvancedOptionsClick : EditItemAction + UndocumentedPublicClass:ExpandableFloatingActionButton.kt$ExpandableFabIcon + UndocumentedPublicClass:ExpandableFloatingActionButton.kt$ExpandableFabOption + UndocumentedPublicClass:ExpandableFloatingActionButton.kt$ExpandableFabState + UndocumentedPublicClass:ExpandableFloatingActionButton.kt$ExpandableFabState$Collapsed : ExpandableFabState + UndocumentedPublicClass:ExpandableFloatingActionButton.kt$ExpandableFabState$Expanded : ExpandableFabState + UndocumentedPublicClass:ExportDataResult.kt$ExportDataResult$Error : ExportDataResult + UndocumentedPublicClass:ExportDataResult.kt$ExportDataResult$Success : ExportDataResult + UndocumentedPublicClass:ExportViewModel.kt$ExportAction + UndocumentedPublicClass:ExportViewModel.kt$ExportAction$CloseButtonClick : ExportAction + UndocumentedPublicClass:ExportViewModel.kt$ExportAction$ConfirmExportClick : ExportAction + UndocumentedPublicClass:ExportViewModel.kt$ExportAction$DialogDismiss : ExportAction + UndocumentedPublicClass:ExportViewModel.kt$ExportAction$ExportFormatOptionSelect : ExportAction + UndocumentedPublicClass:ExportViewModel.kt$ExportAction$ExportLocationReceive : ExportAction + UndocumentedPublicClass:ExportViewModel.kt$ExportAction$Internal : ExportAction + UndocumentedPublicClass:ExportViewModel.kt$ExportAction.Internal$SaveExportDataToUriResultReceive : Internal + UndocumentedPublicClass:ExportViewModel.kt$ExportEvent + UndocumentedPublicClass:ExportViewModel.kt$ExportEvent$NavigateBack : ExportEvent + UndocumentedPublicClass:ExportViewModel.kt$ExportEvent$NavigateToSelectExportDestination : ExportEvent + UndocumentedPublicClass:ExportViewModel.kt$ExportEvent$ShowToast : ExportEvent + UndocumentedPublicClass:ExportViewModel.kt$ExportState + UndocumentedPublicClass:ExportViewModel.kt$ExportState$DialogState + UndocumentedPublicClass:ExportViewModel.kt$ExportState.DialogState$Error : DialogState + UndocumentedPublicClass:ExportViewModel.kt$ExportState.DialogState$Loading : DialogState + UndocumentedPublicClass:ExportViewModel.kt$ExportViewModel : BaseViewModel + UndocumentedPublicClass:FileManagerImpl.kt$FileManagerImpl : FileManager + UndocumentedPublicClass:ImportDataResult.kt$ImportDataResult$Error : ImportDataResult + UndocumentedPublicClass:ImportDataResult.kt$ImportDataResult$Success : ImportDataResult + UndocumentedPublicClass:ItemListingExpandableFabAction.kt$ItemListingExpandableFabAction : ExpandableFabOption + UndocumentedPublicClass:ItemListingExpandableFabAction.kt$ItemListingExpandableFabAction$EnterSetupKey : ItemListingExpandableFabAction + UndocumentedPublicClass:ItemListingExpandableFabAction.kt$ItemListingExpandableFabAction$ScanQrCode : ItemListingExpandableFabAction + UndocumentedPublicClass:ItemListingViewModel.kt$ItemListingState.DialogState$DeleteConfirmationPrompt : DialogState + UndocumentedPublicClass:LastPassJsonExport.kt$LastPassJsonExport + UndocumentedPublicClass:LastPassJsonExport.kt$LastPassJsonExport$Account + UndocumentedPublicClass:LastPassJsonExport.kt$LastPassJsonExport$Folder + UndocumentedPublicClass:LastPassJsonExport.kt$LastPassJsonExport.Account$BackupInfo + UndocumentedPublicClass:LastPassJsonExport.kt$LastPassJsonExport.Account$FolderData + UndocumentedPublicClass:MainActivity.kt$MainActivity : AppCompatActivity + UndocumentedPublicClass:ManualCodeEntryViewModel.kt$ManualCodeEntryState$DialogState : Parcelable + UndocumentedPublicClass:ManualCodeEntryViewModel.kt$ManualCodeEntryState.DialogState$Error : DialogState + UndocumentedPublicClass:ManualCodeEntryViewModel.kt$ManualCodeEntryState.DialogState$Loading : DialogState + UndocumentedPublicClass:PlatformDiskModule.kt$PlatformDiskModule + UndocumentedPublicClass:PlatformNetworkModule.kt$PlatformNetworkModule + UndocumentedPublicClass:RootNavViewModel.kt$RootNavState$NavState : Parcelable + UndocumentedPublicClass:RootNavViewModel.kt$RootNavViewModel : BaseViewModel + UndocumentedPublicClass:SettingsViewModel.kt$SettingsAction.SecurityClick$UnlockWithBiometricToggle : SecurityClick + UndocumentedPublicClass:SettingsViewModel.kt$SettingsState$Dialog : Parcelable + UndocumentedPublicClass:SettingsViewModel.kt$SettingsState.Dialog$Loading : Dialog + UndocumentedPublicClass:TwoFasJsonExport.kt$TwoFasJsonExport + UndocumentedPublicClass:TwoFasJsonExport.kt$TwoFasJsonExport$Group + UndocumentedPublicClass:TwoFasJsonExport.kt$TwoFasJsonExport$Service + UndocumentedPublicClass:TwoFasJsonExport.kt$TwoFasJsonExport.Service$Badge + UndocumentedPublicClass:TwoFasJsonExport.kt$TwoFasJsonExport.Service$Icon + UndocumentedPublicClass:TwoFasJsonExport.kt$TwoFasJsonExport.Service$Order + UndocumentedPublicClass:TwoFasJsonExport.kt$TwoFasJsonExport.Service$Otp + UndocumentedPublicClass:TwoFasJsonExport.kt$TwoFasJsonExport.Service.Icon$IconCollection + UndocumentedPublicClass:TwoFasJsonExport.kt$TwoFasJsonExport.Service.Icon$Label + UndocumentedPublicFunction:AuthenticatorItemAlgorithm.kt$AuthenticatorItemAlgorithm.Companion$fun fromStringOrNull(value: String): AuthenticatorItemAlgorithm? + UndocumentedPublicFunction:AuthenticatorItemEntity.kt$AuthenticatorItemEntity$fun toOtpAuthUriString(): String + UndocumentedPublicFunction:AuthenticatorItemType.kt$AuthenticatorItemType.Companion$fun fromStringOrNull(value: String): AuthenticatorItemType? + UndocumentedPublicFunction:AuthenticatorSdkSource.kt$AuthenticatorSdkSource$suspend fun generateBiometricsKey(): Result<String> + UndocumentedPublicFunction:AuthenticatorTheme.kt$@Composable fun AuthenticatorTheme( theme: AppTheme = AppTheme.DEFAULT, dynamicColor: Boolean = false, content: @Composable () -> Unit, ) + UndocumentedPublicFunction:EditItemScreen.kt$@Composable fun EditItemContent( modifier: Modifier = Modifier, viewState: EditItemState.ViewState.Content, onIssuerNameTextChange: (String) -> Unit = {}, onUsernameTextChange: (String) -> Unit = {}, onToggleFavorite: (Boolean) -> Unit = {}, onTypeOptionClicked: (AuthenticatorItemType) -> Unit = {}, onTotpCodeTextChange: (String) -> Unit = {}, onAlgorithmOptionClicked: (AuthenticatorItemAlgorithm) -> Unit = {}, onRefreshPeriodOptionClicked: (AuthenticatorRefreshPeriodOption) -> Unit = {}, onNumberOfDigitsChanged: (Int) -> Unit = {}, onExpandAdvancedOptionsClicked: () -> Unit = {}, ) + UndocumentedPublicFunction:EditItemViewModel.kt$AuthenticatorRefreshPeriodOption.Companion$fun fromSeconds(seconds: Int) + UndocumentedPublicFunction:ExpandableFloatingActionButton.kt$@Composable fun <T : ExpandableFabOption> ExpandableFloatingActionButton( modifier: Modifier = Modifier, label: Text?, items: List<T>, expandableFabState: MutableState<ExpandableFabState> = rememberExpandableFabState(), expandableFabIcon: ExpandableFabIcon, onStateChange: (expandableFabState: ExpandableFabState) -> Unit = { }, ) + UndocumentedPublicFunction:ExpandableFloatingActionButton.kt$@Composable fun rememberExpandableFabState() + UndocumentedPublicFunction:ExpandableFloatingActionButton.kt$ExpandableFabState$fun isExpanded() + UndocumentedPublicFunction:ExpandableFloatingActionButton.kt$ExpandableFabState$fun toggleValue() + UndocumentedPublicFunction:ExportNavigation.kt$fun NavController.navigateToExport(navOptions: NavOptions? = null) + UndocumentedPublicFunction:ExportNavigation.kt$fun NavGraphBuilder.exportDestination( onNavigateBack: () -> Unit, ) + UndocumentedPublicFunction:ExportScreen.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable fun ExportScreen( viewModel: ExportViewModel = hiltViewModel(), intentManager: IntentManager = LocalIntentManager.current, onNavigateBack: () -> Unit, ) + UndocumentedPublicFunction:ImportingScreen.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable fun ImportingScreen( viewModel: ImportingViewModel = hiltViewModel(), intentManager: IntentManager = LocalIntentManager.current, onNavigateBack: () -> Unit, ) + UndocumentedPublicFunction:ItemListingScreen.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable @Preview(showBackground = true) fun EmptyListingContentPreview() + UndocumentedPublicFunction:SearchHandlers.kt$SearchHandlers.Companion$fun create(viewModel: ItemSearchViewModel): SearchHandlers + UndocumentedPublicFunction:SettingsScreen.kt$@Composable fun SecuritySettings( state: SettingsState, biometricsManager: BiometricsManager = LocalBiometricsManager.current, onBiometricToggle: (Boolean) -> Unit, ) + UndocumentedPublicFunction:SettingsScreen.kt$@Composable fun VaultSettings( modifier: Modifier = Modifier, onExportClick: () -> Unit, onImportClick: () -> Unit, onBackupClick: () -> Unit, ) + UndocumentedPublicFunction:SettingsViewModel.kt$SettingsViewModel.Companion$fun createInitialState( clock: Clock, appLanguage: AppLanguage, appTheme: AppTheme, unlockWithBiometricsEnabled: Boolean, isSubmitCrashLogsEnabled: Boolean, ): SettingsState + UndocumentedPublicFunction:TutorialNavigation.kt$fun NavController.navigateToSettingsTutorial(navOptions: NavOptions? = null) + UndocumentedPublicFunction:TutorialNavigation.kt$fun NavController.navigateToTutorial(navOptions: NavOptions? = null) + UndocumentedPublicFunction:TutorialNavigation.kt$fun NavGraphBuilder.tutorialDestination(onTutorialFinished: () -> Unit) + UndocumentedPublicFunction:TutorialNavigation.kt$fun NavGraphBuilder.tutorialSettingsDestination(onTutorialFinished: () -> Unit) + UndocumentedPublicFunction:TutorialScreen.kt$@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class) @Composable fun TutorialScreen( viewModel: TutorialViewModel = hiltViewModel(), onTutorialFinished: () -> Unit, ) + UndocumentedPublicFunction:TutorialScreen.kt$@Preview @Composable fun TutorialScreenPreview() + UndocumentedPublicFunction:UnlockScreen.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable fun UnlockScreen( viewModel: UnlockViewModel = hiltViewModel(), biometricsManager: BiometricsManager = LocalBiometricsManager.current, onUnlocked: () -> Unit, ) + UndocumentedPublicFunction:VerificationCodeItemExtensions.kt$fun List<VerificationCodeItem>.toViewState( alertThresholdSeconds: Int, ): ItemListingState.ViewState + UndocumentedPublicFunction:VerificationCodeItemExtensions.kt$fun VerificationCodeItem.toDisplayItem(alertThresholdSeconds: Int) + + diff --git a/detekt-config.yml b/detekt-config.yml new file mode 100644 index 0000000000..1a3b30793a --- /dev/null +++ b/detekt-config.yml @@ -0,0 +1,668 @@ +build: + maxIssues: 0 + excludeCorrectable: false + weights: + +config: + validation: true + warningsAsErrors: false + excludes: '' + +processors: + active: true + exclude: + - 'DetektProgressListener' + +console-reports: + active: true + exclude: + - 'ProjectStatisticsReport' + - 'ComplexityReport' + - 'NotificationReport' + - 'FileBasedFindingsReport' + +output-reports: + active: true + exclude: + +comments: + active: true + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**', '**ServiceImpl.kt' ] + AbsentOrWrongFileLicense: + active: false + licenseTemplateFile: 'license.template' + CommentOverPrivateFunction: + active: false + CommentOverPrivateProperty: + active: false + EndOfSentenceFormat: + active: false + endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)' + UndocumentedPublicClass: + active: true + searchInNestedClass: true + searchInInnerClass: true + searchInInnerObject: true + searchInInnerInterface: true + UndocumentedPublicFunction: + active: true + ignoreAnnotated: [ 'Module', 'POST', 'PUT', 'DELETE', 'GET', 'PATCH' ] + UndocumentedPublicProperty: + active: false + +complexity: + active: true + ComplexCondition: + active: true + threshold: 4 + ComplexInterface: + active: false + threshold: 10 + includeStaticDeclarations: false + includePrivateDeclarations: false + CyclomaticComplexMethod: + active: true + threshold: 15 + ignoreSingleWhenExpression: true + ignoreSimpleWhenEntries: false + ignoreNestingFunctions: false + nestingFunctions: [ run, let, apply, with, also, use, forEach, isNotNull, ifNull ] + LabeledExpression: + active: false + ignoredLabels: [ ] + LargeClass: + active: true + threshold: 600 + LongMethod: + active: true + threshold: 60 + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + LongParameterList: + active: true + functionThreshold: 6 + constructorThreshold: 7 + ignoreDefaultParameters: false + ignoreDataClasses: true + ignoreAnnotated: [ 'Composable', 'dagger.Provides' ] + MethodOverloading: + active: false + threshold: 6 + NestedBlockDepth: + active: true + threshold: 4 + ReplaceSafeCallChainWithRun: + active: false + StringLiteralDuplication: + active: false + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + threshold: 3 + ignoreAnnotation: true + excludeStringsWithLessThan5Characters: true + ignoreStringsRegex: '$^' + TooManyFunctions: + active: true + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + thresholdInFiles: 11 + thresholdInClasses: 11 + thresholdInInterfaces: 11 + thresholdInObjects: 11 + thresholdInEnums: 11 + ignoreDeprecated: false + ignorePrivate: false + ignoreOverridden: false + ignoreAnnotated: [ 'Module' ] + +coroutines: + active: true + GlobalCoroutineUsage: + active: false + RedundantSuspendModifier: + active: false + SuspendFunWithFlowReturnType: + active: false + +empty-blocks: + active: true + EmptyCatchBlock: + active: true + allowedExceptionNameRegex: '_|(ignore|expected).*' + EmptyClassBlock: + active: true + EmptyDefaultConstructor: + active: true + EmptyDoWhileBlock: + active: true + EmptyElseBlock: + active: true + EmptyFinallyBlock: + active: true + EmptyForBlock: + active: true + EmptyFunctionBlock: + active: true + ignoreOverridden: false + EmptyIfBlock: + active: true + EmptyInitBlock: + active: true + EmptyKtFile: + active: true + EmptySecondaryConstructor: + active: true + EmptyTryBlock: + active: true + EmptyWhenBlock: + active: true + EmptyWhileBlock: + active: true + +exceptions: + active: true + ExceptionRaisedInUnexpectedLocation: + active: false + methodNames: [ toString, hashCode, equals, finalize ] + InstanceOfCheckForException: + active: false + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + NotImplementedDeclaration: + active: false + PrintStackTrace: + active: false + RethrowCaughtException: + active: false + ReturnFromFinally: + active: false + ignoreLabeled: false + SwallowedException: + active: false + ignoredExceptionTypes: + - InterruptedException + - NumberFormatException + - ParseException + - MalformedURLException + allowedExceptionNameRegex: '_|(ignore|expected).*' + ThrowingExceptionFromFinally: + active: false + ThrowingExceptionInMain: + active: false + ThrowingExceptionsWithoutMessageOrCause: + active: false + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + exceptions: + - IllegalArgumentException + - IllegalStateException + - IOException + ThrowingNewInstanceOfSameException: + active: false + TooGenericExceptionCaught: + active: true + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + exceptionNames: + - ArrayIndexOutOfBoundsException + - Error + - Exception + - IllegalMonitorStateException + - NullPointerException + - IndexOutOfBoundsException + - RuntimeException + - Throwable + allowedExceptionNameRegex: '_|(ignore|expected).*' + TooGenericExceptionThrown: + active: true + exceptionNames: + - Error + - Exception + - Throwable + - RuntimeException + +formatting: + active: true + android: false + autoCorrect: true + AnnotationOnSeparateLine: + active: false + autoCorrect: true + AnnotationSpacing: + active: false + autoCorrect: true + ArgumentListWrapping: + active: false + autoCorrect: true + ChainWrapping: + active: true + autoCorrect: true + CommentSpacing: + active: true + autoCorrect: true + EnumEntryNameCase: + active: false + autoCorrect: true + Filename: + active: false + FinalNewline: + active: true + autoCorrect: true + insertFinalNewLine: true + ImportOrdering: + active: false + autoCorrect: true + layout: 'idea' + Indentation: + active: false + autoCorrect: true + indentSize: 4 + MaximumLineLength: + active: true + maxLineLength: 120 + ModifierOrdering: + active: true + autoCorrect: true + MultiLineIfElse: + active: true + autoCorrect: true + NoBlankLineBeforeRbrace: + active: true + autoCorrect: true + NoConsecutiveBlankLines: + active: true + autoCorrect: true + NoEmptyClassBody: + active: true + autoCorrect: true + NoEmptyFirstLineInMethodBlock: + active: false + autoCorrect: true + NoLineBreakAfterElse: + active: true + autoCorrect: true + NoLineBreakBeforeAssignment: + active: true + autoCorrect: true + NoMultipleSpaces: + active: true + autoCorrect: true + NoSemicolons: + active: true + autoCorrect: true + NoTrailingSpaces: + active: true + autoCorrect: true + NoUnitReturn: + active: true + autoCorrect: true + NoUnusedImports: + active: true + autoCorrect: true + NoWildcardImports: + active: true + PackageName: + active: true + autoCorrect: true + ParameterListWrapping: + active: true + autoCorrect: true + SpacingAroundColon: + active: true + autoCorrect: true + SpacingAroundComma: + active: true + autoCorrect: true + SpacingAroundCurly: + active: true + autoCorrect: true + SpacingAroundDot: + active: true + autoCorrect: true + SpacingAroundDoubleColon: + active: false + autoCorrect: true + SpacingAroundKeyword: + active: true + autoCorrect: true + SpacingAroundOperators: + active: true + autoCorrect: true + SpacingAroundParens: + active: true + autoCorrect: true + SpacingAroundRangeOperator: + active: true + autoCorrect: true + SpacingBetweenDeclarationsWithAnnotations: + active: false + autoCorrect: true + SpacingBetweenDeclarationsWithComments: + active: false + autoCorrect: true + StringTemplate: + active: true + autoCorrect: true + TrailingCommaOnCallSite: + active: true + autoCorrect: true + useTrailingCommaOnCallSite: true + TrailingCommaOnDeclarationSite: + active: true + autoCorrect: true + useTrailingCommaOnDeclarationSite: true + +libraries: + active: true + ForbiddenPublicDataClass: + active: false + LibraryCodeMustSpecifyReturnType: + active: true + LibraryEntitiesShouldNotBePublic: + active: false + +naming: + active: true + ClassNaming: + active: true + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + classPattern: '[A-Z][a-zA-Z0-9]*' + ConstructorParameterNaming: + active: true + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + parameterPattern: '[a-z][A-Za-z0-9]*' + privateParameterPattern: '[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + EnumNaming: + active: true + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + enumEntryPattern: '[A-Z][_a-zA-Z0-9]*' + ForbiddenClassName: + active: false + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + forbiddenName: [ ] + FunctionMaxLength: + active: false + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + maximumFunctionNameLength: 30 + FunctionMinLength: + active: false + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + minimumFunctionNameLength: 3 + FunctionNaming: + active: true + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + functionPattern: '([a-z][a-zA-Z0-9]*)|(`.*`)' + excludeClassPattern: '$^' + ignoreAnnotated: [ 'Composable' ] + FunctionParameterNaming: + active: true + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + parameterPattern: '[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + InvalidPackageDeclaration: + active: false + rootPackage: '' + MatchingDeclarationName: + active: true + mustBeFirst: true + MemberNameEqualsClassName: + active: true + NonBooleanPropertyPrefixedWithIs: + active: false + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + ObjectPropertyNaming: + active: true + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + constantPattern: '[A-Za-z][_A-Za-z0-9]*' + propertyPattern: '[A-Za-z][_A-Za-z0-9]*' + privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*' + PackageNaming: + active: true + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*' + TopLevelPropertyNaming: + active: true + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**', '**/compose/**' ] + constantPattern: '[A-Z][_A-Z0-9]*' + propertyPattern: '[A-Za-z][_A-Za-z0-9]*' + privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*' + VariableMaxLength: + active: false + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + maximumVariableNameLength: 64 + VariableMinLength: + active: false + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + minimumVariableNameLength: 1 + VariableNaming: + active: true + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + variablePattern: '[a-z][A-Za-z0-9]*' + privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + +performance: + active: true + ArrayPrimitive: + active: true + ForEachOnRange: + active: true + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + SpreadOperator: + active: false + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + UnnecessaryTemporaryInstantiation: + active: true + +potential-bugs: + active: true + Deprecation: + active: false + EqualsAlwaysReturnsTrueOrFalse: + active: true + EqualsWithHashCodeExist: + active: true + ExplicitGarbageCollectionCall: + active: true + HasPlatformType: + active: false + IgnoredReturnValue: + active: false + restrictToConfig: true + returnValueAnnotations: [ '*.CheckReturnValue', '*.CheckResult' ] + ImplicitDefaultLocale: + active: false + ImplicitUnitReturnType: + active: false + allowExplicitReturnType: true + InvalidRange: + active: true + IteratorHasNextCallsNextMethod: + active: true + IteratorNotThrowingNoSuchElementException: + active: true + LateinitUsage: + active: false + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + ignoreAnnotated: [ ] + ignoreOnClassesPattern: '' + MapGetWithNotNullAssertionOperator: + active: false + NullableToStringCall: + active: false + UnconditionalJumpStatementInLoop: + active: false + UnnecessaryNotNullOperator: + active: false + UnnecessarySafeCall: + active: false + UnreachableCode: + active: true + UnsafeCallOnNullableType: + active: true + UnsafeCast: + active: false + UselessPostfixExpression: + active: false + WrongEqualsTypeParameter: + active: true + +style: + active: true + BracesOnIfStatements: + active: false + ClassOrdering: + active: false + CollapsibleIfStatements: + active: false + DataClassContainsFunctions: + active: false + conversionFunctionPrefix: [ 'to' ] + DataClassShouldBeImmutable: + active: false + EqualsNullCall: + active: true + EqualsOnSignatureLine: + active: false + ExplicitCollectionElementAccessMethod: + active: false + ExplicitItLambdaParameter: + active: false + ExpressionBodySyntax: + active: false + includeLineWrapping: false + ForbiddenComment: + active: true + comments: [ 'FIXME:', 'STOPSHIP:' ] + allowedPatterns: '' + ForbiddenImport: + active: false + imports: [ ] + forbiddenPatterns: '' + ForbiddenMethodCall: + active: false + methods: [ 'kotlin.io.println', 'kotlin.io.print' ] + ForbiddenVoid: + active: false + ignoreOverridden: false + ignoreUsageInGenerics: false + FunctionOnlyReturningConstant: + active: true + ignoreOverridableFunction: true + excludedFunctions: [ 'describeContents' ] + ignoreAnnotated: [ 'dagger.Provides' ] + LoopWithTooManyJumpStatements: + active: true + maxJumpCount: 1 + MagicNumber: + active: true + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + ignoreNumbers: [ '-1', '0', '0.5', '1', '2' ] + ignoreHashCodeFunction: true + ignorePropertyDeclaration: false + ignoreLocalVariableDeclaration: false + ignoreConstantDeclaration: true + ignoreCompanionObjectPropertyDeclaration: true + ignoreAnnotation: false + ignoreNamedArgument: true + ignoreEnums: false + ignoreRanges: false + MandatoryBracesLoops: + active: false + MaxLineLength: + active: true + maxLineLength: 100 + excludePackageStatements: true + excludeImportStatements: true + excludeCommentStatements: false + MayBeConst: + active: true + ModifierOrder: + active: true + NestedClassesVisibility: + active: false + NewLineAtEndOfFile: + active: true + NoTabs: + active: false + OptionalAbstractKeyword: + active: true + OptionalUnit: + active: false + BracesOnWhenStatements: + active: false + PreferToOverPairSyntax: + active: false + ProtectedMemberInFinalClass: + active: true + RedundantExplicitType: + active: false + RedundantVisibilityModifierRule: + active: false + ReturnCount: + active: false + max: 2 + excludedFunctions: [ 'equals' ] + excludeLabeled: false + excludeReturnFromLambda: true + excludeGuardClauses: false + SafeCast: + active: true + SerialVersionUIDInSerializableClass: + active: false + SpacingBetweenPackageAndImports: + active: false + ThrowsCount: + active: true + max: 2 + TrailingWhitespace: + active: false + UnderscoresInNumericLiterals: + active: false + acceptableLength: 5 + UnnecessaryAbstractClass: + active: true + ignoreAnnotated: [ 'Module' ] + UnnecessaryAnnotationUseSiteTarget: + active: false + UnnecessaryApply: + active: false + UnnecessaryInheritance: + active: true + UnnecessaryLet: + active: false + UnnecessaryParentheses: + active: false + UntilInsteadOfRangeTo: + active: false + UnusedImports: + active: false + UnusedPrivateClass: + active: true + UnusedPrivateMember: + active: false + allowedNames: '(_|ignored|expected|serialVersionUID)' + UseArrayLiteralsInAnnotations: + active: false + UseCheckNotNull: + active: false + UseCheckOrError: + active: false + UseDataClass: + active: false + ignoreAnnotated: [ ] + allowVars: false + UseEmptyCounterpart: + active: false + UseIfInsteadOfWhen: + active: false + UseRequire: + active: false + UseRequireNotNull: + active: false + UselessCallOnNotNull: + active: true + UtilityClassWithPublicConstructor: + active: true + ignoreAnnotated: [ 'Module' ] + VarCouldBeVal: + active: false + WildcardImport: + active: true + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + excludeImports: [ 'java.util.*', 'kotlinx.android.synthetic.*' ] diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ed5bbc4ef9..18560e7108 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -28,6 +28,7 @@ androdixAutofill = "1.1.0" androidxWork = "2.9.1" bitwardenSdk = "0.4.0-20240314.115913-173" crashlytics = "3.0.2" +detekt = "1.23.6" espresso = "3.6.1" fastlaneScreengrab = "2.1.1" firebaseBom = "33.2.0" @@ -91,6 +92,8 @@ androidx-test-espresso = { module = "androidx.test.espresso:espresso-core", vers androidx-work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "androidxWork" } bitwarden-sdk = { module = "com.bitwarden:sdk-android", version.ref = "bitwardenSdk" } bumptech-glide = { module = "com.github.bumptech.glide:compose", version.ref = "glide" } +detekt-detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" } +detekt-detekt-rules = { module = "io.gitlab.arturbosch.detekt:detekt-rules-libraries", version.ref = "detekt" } fastlane-screengrab = { module = "tools.fastlane:screengrab", version.ref = "fastlaneScreengrab" } google-firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBom" } google-firebase-cloud-messaging = { module = "com.google.firebase:firebase-messaging-ktx" } @@ -122,6 +125,7 @@ zxing-zxing-core = { module = "com.google.zxing:core", version.ref = "zxing" } [plugins] android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" } crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "crashlytics" } +detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } google-protobuf = { id = "com.google.protobuf", version.ref = "googleProtoBufPlugin" } google-services = { id = "com.google.gms.google-services", version.ref = "googleServices" } hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }