[ANDROID] [JETPACK COMPOSE] [% DUPICATION] SonarCloud is getting compose widgets as duplicated code

  • ALM used * (GitHub)
  • Languages of the repository (Kotlin)
  • Error observed
    *** SonarCloud Github integration is getting jetpack compose code widgets as % of code duplication, as shown below. Causing PR’s/commits to be wrongly reproved.

Hello @joaquim-og,

Thanks for the report and the patience! This indeed does not look correct.

I tried reproducing this with some dummy code, however without success. Could you provide us with a compilable reproducer to investigate this further?

Hey Johann!

Sure, in the snippet below I replace some sensitive Data with “XXX”, but set some regions where sonarCloud is considering duplicated code:

class XXXFragment : BaseFragment() {
    private val viewModel by sharedViewModel<XXXX>()

    //START 1 - Sonar cloud is getting all this block as duplicated
    @OptIn(
        ExperimentalUnitApi::class,
        ExperimentalMaterialApi::class,
        ExperimentalPagerApi::class,
        ExperimentalAnimationApi::class
    )
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return ComposeView(requireContext()).apply {
            setContent {
                ScreenContent()
            }
        }
    }
    @ExperimentalUnitApi
    @ExperimentalMaterialApi
    @ExperimentalPagerApi
    @ExperimentalAnimationApi
    @Composable
    fun ScreenContent() {
        val scrollState = rememberScrollState()
        ColumnGradient(
            withMiddleColor = true,
            modifier = Modifier.fillMaxSize().verticalScroll(scrollState),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Top,
            colorStart = colorResource(R.color.ds_brand_blue_softer),
            colorMiddle = colorResource(R.color.ds_brand_blue_softer),
            colorEnd = colorResource(R.color.ds_white)

    //END 1 - Sonar cloud is getting all this block as duplicated
        ) {
            XXXIcon(
                color = R.color.ds_black_pure,
                resource = R.drawable.ic_x,
                modifier = Modifier.align(Alignment.End).padding(end = XXX.base, top = XXX.base)
                    .clickable(onClick = { finishActivity() })
            )
            with(viewModel.XXXResult.getValue()) {
                Text(
                    fontWeight = FontWeight.Normal,
                    textAlign = TextAlign.Center,
                    style = MaterialTheme.typography.body1.copy(color = colorResource(R.color.ds_brand_blue_dark)),
                    text = stringResource(R.string.XXX).replace(
                        "#XXXNamePlaceholder", this?.name.toString()
                    ),
                    modifier = Modifier.padding(
                        top = XXXSpacing.regular, start = XXXSpacing.large, end = XXXSpacing.large
                    ).align(Alignment.CenterHorizontally)
                )
            }
            XXXImage(
                image = R.drawable.ic_XXXX, modifier = Modifier.padding(bottom = XXXSpacing.large)
            )
            Text(
                fontWeight = FontWeight.Bold,
                style = MaterialTheme.typography.h4.copy(color = colorResource(R.color.ds_black_pure)),
                textAlign = TextAlign.Start,
                text = stringResource(R.string.XXXX_title),
                modifier = Modifier.padding(start = XXXSpacing.large, end = XXXSpacing.large).align(Alignment.Start)
            )
            with(viewModel.XXXX.getValue()) {
                val XXXDate = this?.XXXDate?.convertEpochDayToDatePattern(
                    dayMonthYearFormat
                )
                val XXDate = this?.XXDate?.convertEpochDayToDatePattern(dayMonthYearFormat)

    //START 2 - Sonar cloud is getting all this block as duplicated
                Text(
                    fontWeight = FontWeight.Normal,
                    style = MaterialTheme.typography.body1.copy(color = colorResource(R.color.ds_grey_medium)),
                    textAlign = TextAlign.Start,
                    text = buildAnnotatedString {
                        val blockText = stringResource(R.string.XXX_1).replace(
                            "#XXXXholder", "$XXXDate - $XXEndDate"
                        )
                        append(blockText)
                        addStyle(
                            style = SpanStyle(
                                color = colorResource(R.color.ds_grey_medium), fontWeight = FontWeight.Bold
                            ), start = 80, end = blockText.length
                        )
                    },
                    modifier = Modifier.padding(
                        top = XXXSpacing.small,
                        end = XXXSpacing.regular,
                        start = XXXSpacing.regular,
                    ).align(Alignment.Start)
                )
                Spacer(modifier = Modifier.padding(top = XXXSpacing.mini))
                Text(
                    fontWeight = FontWeight.Normal,
                    textAlign = TextAlign.Start,
                    style = MaterialTheme.typography.body1.copy(color = colorResource(R.color.ds_grey_medium)),
                    text = stringResource(R.string.XXXX_2),
                    modifier = Modifier.padding(
                        end = XXXSpacing.regular,
                        bottom = XXXSpacing.regular,
                        start = XXXSpacing.regular,
                    ).align(Alignment.Start)
                )
            }
    //END 2 - Sonar cloud is getting all this block as duplicated

            XXXButtonAction(text = stringResource(R.string.XXX_proceed),
                modifier = Modifier.padding(start = XXXSpacing.regular, end = XXXSpacing.regular)
                    .align(Alignment.CenterHorizontally),
                iconVectorResource = R.drawable.XXX_right,
                showIconRight = true,
                onClick = {
                    navigateToXXXX()
                })
            Spacer(
                modifier = Modifier.height(XXXSpacing.mini)
            )
        }
    }
    private fun navigateToXXX() {
        navigateSafe(R.id.action_XXX_to_XXX)
    }
    private fun finishActivity() {
        requireActivity().finish()
    }
    override fun setupViews() {
    }
    override fun setupNavigation(view: View) {
    }
}

Thanks for providing the code snippet. I’ve scanned it in an attempt to reproduce, however, wasn’t able to. My difficulties here may be related to the fact that the code fragment itself does not compile without additional dependencies. I do not have the same environment set up as you for this project, so I cannot easily use this snippet for an in-depth analysis.

Is it possible for you to reproduce the issue by creating a fragment that will compile without requiring additional source code or external dependencies? Alternatively, you could create a small demo project on e.g. GitHub that is set up in such a way that the required dependencies are present.