diff --git a/buildSrc/src/main/java/detekt-convention.gradle.kts b/buildSrc/src/main/java/detekt-convention.gradle.kts index 3509e1a74c..c78ee08e8e 100644 --- a/buildSrc/src/main/java/detekt-convention.gradle.kts +++ b/buildSrc/src/main/java/detekt-convention.gradle.kts @@ -4,19 +4,23 @@ plugins { id("io.gitlab.arturbosch.detekt") } -tasks.withType { +detekt { parallel = true ignoreFailures = false autoCorrect = true buildUponDefaultConfig = true - config.setFrom(rootProject.files("config/detekt/detekt.yml")) +} + +tasks.withType { include("**/*.kt") exclude("**/resources/**", "**/build/**") reports { + sarif { + required.set(false) + } txt { required.set(true) - outputLocation.set(rootProject.file("build/reports/detekt.txt")) } } diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index 570cb574d9..f26f48c71c 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -10,13 +10,21 @@ processors: console-reports: active: true + exclude: + - 'ProjectStatisticsReport' + - 'ComplexityReport' + - 'NotificationReport' + - 'FindingsReport' + - 'FileBasedFindingsReport' +# - 'LiteFindingsReport' output-reports: active: true exclude: - # - 'TxtOutputReport' - - 'XmlOutputReport' - 'HtmlOutputReport' + # - 'TxtOutputReport' + - 'XmlOutputReport' + - 'SarifOutputReport' - 'MdOutputReport' comments: diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/Dialogs.kt b/core/ui/src/main/java/com/tangem/core/ui/components/Dialogs.kt index 993ae27a45..aaf52b686e 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/Dialogs.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/Dialogs.kt @@ -104,9 +104,9 @@ fun TextInputDialog( confirmButton: DialogButton, onDismissDialog: () -> Unit, onValueChange: (TextFieldValue) -> Unit, + textFieldParams: AdditionalTextInputDialogParams, title: String? = null, dismissButton: DialogButton? = null, - textFieldParams: AdditionalTextInputDialogParams, ) { TangemDialog( type = DialogType.TextInput( @@ -160,8 +160,8 @@ private fun TangemDialog( @Composable private fun DialogContent( - modifier: Modifier = Modifier, type: DialogType, + modifier: Modifier = Modifier, ) { Column( modifier = modifier, @@ -197,9 +197,9 @@ private fun DialogContent( @Composable private fun DialogButtons( - modifier: Modifier = Modifier, confirmButton: DialogButton, dismissButton: DialogButton?, + modifier: Modifier = Modifier, ) { Row( modifier = modifier.fillMaxWidth(), @@ -227,11 +227,11 @@ private fun DialogButtons( @Composable private fun DialogButton( - modifier: Modifier = Modifier, text: String, warning: Boolean, enabled: Boolean, onClick: () -> Unit, + modifier: Modifier = Modifier, ) { Box(modifier = modifier) { if (warning) { diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithBackButton.kt b/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithBackButton.kt index 855fcbbc34..5d52a7bc10 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithBackButton.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithBackButton.kt @@ -22,8 +22,10 @@ import com.tangem.core.ui.res.TangemTheme /** * App bar with back button and optional title * - * @param text optional title * @param onBackClick the lambda to be invoked when this icon is pressed + * @param modifier modifier + * @param text optional title + * @param iconRes icon res id * * @see Figma component @@ -31,11 +33,12 @@ import com.tangem.core.ui.res.TangemTheme @Composable fun AppBarWithBackButton( onBackClick: () -> Unit, + modifier: Modifier = Modifier, text: String? = null, @DrawableRes iconRes: Int? = null, ) { Row( - modifier = Modifier + modifier = modifier .background(color = TangemTheme.colors.background.secondary) .fillMaxWidth() .padding(all = dimensionResource(R.dimen.spacing16)), diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt index 4e453421ae..a681a0239b 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt @@ -74,6 +74,7 @@ data class TangemDimens internal constructor( val spacing12: Dp = 12.dp, val spacing14: Dp = 14.dp, val spacing16: Dp = 16.dp, + val spacing18: Dp = 18.dp, val spacing20: Dp = 20.dp, val spacing24: Dp = 24.dp, val spacing28: Dp = 28.dp, diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/SwapRepositoryImpl.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/SwapRepositoryImpl.kt index 2fe85a6563..9f12a6dcad 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/SwapRepositoryImpl.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/SwapRepositoryImpl.kt @@ -54,8 +54,7 @@ internal class SwapRepositoryImpl @Inject constructor( networkId: String, fromTokenAddress: String, toTokenAddress: String, - amount: - String, + amount: String, ): AggregatedSwapDataModel { return withContext(coroutineDispatcher.io) { try { diff --git a/features/tester/impl/.gitignore b/features/tester/impl/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/features/tester/impl/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/features/tester/impl/build.gradle.kts b/features/tester/impl/build.gradle.kts new file mode 100644 index 0000000000..8466265b89 --- /dev/null +++ b/features/tester/impl/build.gradle.kts @@ -0,0 +1,65 @@ +plugins { + id("com.android.library") + kotlin("android") + kotlin("kapt") + id("com.google.dagger.hilt.android") +} + +android { + defaultConfig { + compileSdk = AppConfig.compileSdkVersion + minSdk = AppConfig.minSdkVersion + targetSdk = AppConfig.targetSdkVersion + vectorDrawables { + useSupportLibrary = true + } + } + + kotlinOptions { + jvmTarget = "1.8" + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + buildTypes { + create("debug_beta") { + initWith(getByName("release")) + BuildConfigFieldFactory( + fields = listOf( + Field.Environment("release"), + Field.TestActionEnabled(true), + Field.LogEnabled(true), + ), + builder = ::buildConfigField, + ).create() + } + } + + buildFeatures { + compose = true + } + + composeOptions { + kotlinCompilerExtensionVersion = Versions.compose + } +} + +dependencies { + /** AndroidX */ + implementation(AndroidX.activityCompose) + + /** Compose */ + implementation(Compose.material) + implementation(Compose.ui) + implementation(Compose.uiTooling) + + /** DI */ + implementation(Library.hilt) + kapt(Library.hiltKapt) + + /** Core modules */ + implementation(project(":core:ui")) +} \ No newline at end of file diff --git a/features/tester/impl/src/main/AndroidManifest.xml b/features/tester/impl/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..57f9021fbf --- /dev/null +++ b/features/tester/impl/src/main/AndroidManifest.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/TesterActivity.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/TesterActivity.kt new file mode 100644 index 0000000000..fb1f0e1777 --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/TesterActivity.kt @@ -0,0 +1,17 @@ +package com.tangem.feature.tester.presentation + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import dagger.hilt.android.AndroidEntryPoint + +/** Activity for testers */ +@AndroidEntryPoint +internal class TesterActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + // TODO("2949") add logic to screens + } + } +} \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/featuretoggles/models/TesterFeatureToggle.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/featuretoggles/models/TesterFeatureToggle.kt new file mode 100644 index 0000000000..b898f31db6 --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/featuretoggles/models/TesterFeatureToggle.kt @@ -0,0 +1,14 @@ +package com.tangem.feature.tester.presentation.featuretoggles.models + +/** + * Presentation model of feature toggle + * + * @property name name + * @property isEnabled availability + * +[REDACTED_AUTHOR] + */ +data class TesterFeatureToggle( + val name: String, + val isEnabled: Boolean +) \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/featuretoggles/state/FeatureTogglesStateHolder.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/featuretoggles/state/FeatureTogglesStateHolder.kt new file mode 100644 index 0000000000..5dea9cf196 --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/featuretoggles/state/FeatureTogglesStateHolder.kt @@ -0,0 +1,24 @@ +package com.tangem.feature.tester.presentation.featuretoggles.state + +import com.tangem.feature.tester.presentation.featuretoggles.models.TesterFeatureToggle + +/** + * State holder of feature toggles screen + * +[REDACTED_AUTHOR] + */ +sealed interface FeatureTogglesStateHolder { + + /** + * Content + * + * @property featureToggles feature toggles list + * @property onToggleValueChange the lambda to be invoked when switch button is pressed + * @property onBackClicked the lambda to be invoked when back button is pressed + */ + data class Content( + val onBackClicked: () -> Unit, + val featureToggles: List, + val onToggleValueChange: (String, Boolean) -> Unit, + ) : FeatureTogglesStateHolder +} \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/featuretoggles/ui/FeatureTogglesScreen.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/featuretoggles/ui/FeatureTogglesScreen.kt new file mode 100644 index 0000000000..6d9680804e --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/featuretoggles/ui/FeatureTogglesScreen.kt @@ -0,0 +1,119 @@ +package com.tangem.feature.tester.presentation.featuretoggles.ui + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material.Switch +import androidx.compose.material.SwitchDefaults +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import com.tangem.core.ui.components.appbar.AppBarWithBackButton +import com.tangem.core.ui.res.TangemTheme +import com.tangem.feature.tester.impl.R +import com.tangem.feature.tester.presentation.featuretoggles.models.TesterFeatureToggle +import com.tangem.feature.tester.presentation.featuretoggles.state.FeatureTogglesStateHolder + +/** + * Screen with feature toggles list + * + * @param stateHolder screen state + */ +@Composable +fun FeatureTogglesScreen(stateHolder: FeatureTogglesStateHolder) { + when (stateHolder) { + is FeatureTogglesStateHolder.Content -> FeatureTogglesContent(content = stateHolder) + } +} + +@OptIn(ExperimentalFoundationApi::class) +@Composable +private fun FeatureTogglesContent(content: FeatureTogglesStateHolder.Content) { + LazyColumn( + modifier = Modifier + .fillMaxSize() + .background(TangemTheme.colors.background.secondary), + ) { + stickyHeader { + AppBarWithBackButton( + onBackClick = content.onBackClicked, + text = stringResource(id = R.string.feature_toggles), + ) + } + items(content.featureToggles) { featureToggle -> + FeatureToggleItem( + toggle = featureToggle, + onCheckedChange = { isChange -> content.onToggleValueChange(featureToggle.name, isChange) }, + ) + } + } +} + +@Composable +private fun FeatureToggleItem(toggle: TesterFeatureToggle, onCheckedChange: (Boolean) -> Unit) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = TangemTheme.dimens.spacing18), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = toggle.name, + color = TangemTheme.colors.text.primary1, + maxLines = 1, + style = TangemTheme.typography.body1, + ) + Switch( + checked = toggle.isEnabled, + onCheckedChange = onCheckedChange, + colors = SwitchDefaults.colors( + checkedThumbColor = TangemTheme.colors.control.checked, + uncheckedThumbColor = TangemTheme.colors.control.unchecked, + ), + ) + } +} + +@Preview +@Composable +private fun PreviewFeatureTogglesScreen_InLightTheme() { + TangemTheme(isDark = false) { + FeatureTogglesScreen( + stateHolder = FeatureTogglesStateHolder.Content( + featureToggles = listOf( + TesterFeatureToggle(name = "FEATURE_TOGGLE_1", isEnabled = true), + TesterFeatureToggle(name = "FEATURE_TOGGLE_2", isEnabled = false), + ), + onToggleValueChange = { _, _ -> }, + onBackClicked = {}, + ), + ) + } +} + +@Preview +@Composable +private fun PreviewFeatureTogglesScreen_InDarkTheme() { + TangemTheme(isDark = true) { + FeatureTogglesScreen( + stateHolder = FeatureTogglesStateHolder.Content( + featureToggles = listOf( + TesterFeatureToggle(name = "FEATURE_TOGGLE_1", isEnabled = true), + TesterFeatureToggle(name = "FEATURE_TOGGLE_2", isEnabled = false), + ), + onToggleValueChange = { _, _ -> }, + onBackClicked = {}, + ), + ) + } +} \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/menu/state/TesterMenuStateHolder.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/menu/state/TesterMenuStateHolder.kt new file mode 100644 index 0000000000..b2afffb27b --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/menu/state/TesterMenuStateHolder.kt @@ -0,0 +1,20 @@ +package com.tangem.feature.tester.presentation.menu.state + +/** + * State holder of tester menu screen + * +[REDACTED_AUTHOR] + */ +sealed interface TesterMenuStateHolder { + + /** + * Content + * + * @property onBackClicked the lambda to be invoked when back button is pressed + * @property onFeatureTogglesClicked the lambda to be invoked when feature toggles button is pressed + */ + data class Content( + val onBackClicked: () -> Unit, + val onFeatureTogglesClicked: () -> Unit + ) : TesterMenuStateHolder +} \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/menu/ui/TesterMenuScreen.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/menu/ui/TesterMenuScreen.kt new file mode 100644 index 0000000000..08b73b4e26 --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/menu/ui/TesterMenuScreen.kt @@ -0,0 +1,92 @@ +package com.tangem.feature.tester.presentation.menu.ui + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import com.tangem.core.ui.components.PrimaryButton +import com.tangem.core.ui.components.appbar.AppBarWithBackButton +import com.tangem.core.ui.res.TangemTheme +import com.tangem.feature.tester.impl.R +import com.tangem.feature.tester.presentation.menu.state.TesterMenuStateHolder + +/** + * Screen with functionality for testers + * + * @param stateHolder screen state + */ +@Composable +fun TesterMenuScreen(stateHolder: TesterMenuStateHolder) { + when (stateHolder) { + is TesterMenuStateHolder.Content -> TesterMenuContent(content = stateHolder) + } +} + +@Composable +private fun TesterMenuContent(content: TesterMenuStateHolder.Content) { + Column( + modifier = Modifier + .fillMaxSize() + .background(TangemTheme.colors.background.secondary), + ) { + AppBarWithBackButton( + onBackClick = content.onBackClicked, + text = stringResource(id = R.string.tester_menu), + ) + Column( + modifier = Modifier + .padding( + horizontal = TangemTheme.dimens.spacing18, + vertical = TangemTheme.dimens.spacing8, + ) + .verticalScroll(rememberScrollState()), + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing16), + ) { + PrimaryButton( + text = stringResource(R.string.feature_toggles), + onClick = content.onFeatureTogglesClicked, + modifier = Modifier.fillMaxWidth(), + ) + PrimaryButton( + text = stringResource(R.string.stand_toggles), + onClick = content.onFeatureTogglesClicked, + modifier = Modifier.fillMaxWidth(), + enabled = false, + ) + } + } +} + +@Preview +@Composable +private fun PreviewTesterMenuScreen_InLightTheme() { + TangemTheme(isDark = false) { + TesterMenuScreen( + stateHolder = TesterMenuStateHolder.Content( + onBackClicked = {}, + onFeatureTogglesClicked = {}, + ), + ) + } +} + +@Preview +@Composable +private fun PreviewTesterMenuScreen_InDarkTheme() { + TangemTheme(isDark = true) { + TesterMenuScreen( + stateHolder = TesterMenuStateHolder.Content( + onBackClicked = {}, + onFeatureTogglesClicked = {}, + ), + ) + } +} \ No newline at end of file diff --git a/features/tester/impl/src/main/res/values/strings.xml b/features/tester/impl/src/main/res/values/strings.xml new file mode 100644 index 0000000000..55e295ff67 --- /dev/null +++ b/features/tester/impl/src/main/res/values/strings.xml @@ -0,0 +1,6 @@ + + + Tester menu + Feature toggles + Stand toggles + \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index b7dc831ec1..75add91dd0 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -22,4 +22,6 @@ include(":features:referral:presentation") include(":features:swap:data") include(":features:swap:domain") include(":features:swap:presentation") + +include(":features:tester:impl") // endregion Feature modules \ No newline at end of file