Related
For some reason, the preview shows a different palette than the one I have in Color.kt and Theme.kt. ie. Primary is Color(0xFFA43D00) in Color.kt, but #00658D in the Layout inspector.
I probably messed up something. I didn't like the default palette, so I went to Material Theme Builder and made a custom one. Exported as Jetpack Compose, opened the file, and copy/pasted the contents color information form the exported Theme.kt to the ui/theme/Theme.kt file (without touching anything else, like the package name or the Theme composable):
private val LightColorScheme = lightColorScheme(
primary = md_theme_light_primary,
onPrimary = md_theme_light_onPrimary,
primaryContainer = md_theme_light_primaryContainer,
onPrimaryContainer = md_theme_light_onPrimaryContainer,
secondary = md_theme_light_secondary,
onSecondary = md_theme_light_onSecondary,
secondaryContainer = md_theme_light_secondaryContainer,
onSecondaryContainer = md_theme_light_onSecondaryContainer,
tertiary = md_theme_light_tertiary,
onTertiary = md_theme_light_onTertiary,
tertiaryContainer = md_theme_light_tertiaryContainer,
onTertiaryContainer = md_theme_light_onTertiaryContainer,
error = md_theme_light_error,
errorContainer = md_theme_light_errorContainer,
onError = md_theme_light_onError,
onErrorContainer = md_theme_light_onErrorContainer,
background = md_theme_light_background,
onBackground = md_theme_light_onBackground,
surface = md_theme_light_surface,
onSurface = md_theme_light_onSurface,
surfaceVariant = md_theme_light_surfaceVariant,
onSurfaceVariant = md_theme_light_onSurfaceVariant,
outline = md_theme_light_outline,
inverseOnSurface = md_theme_light_inverseOnSurface,
inverseSurface = md_theme_light_inverseSurface,
inversePrimary = md_theme_light_inversePrimary,
surfaceTint = md_theme_light_surfaceTint,
outlineVariant = md_theme_light_outlineVariant,
scrim = md_theme_light_scrim,
)
private val DarkColorScheme = darkColorScheme(
primary = md_theme_dark_primary,
onPrimary = md_theme_dark_onPrimary,
primaryContainer = md_theme_dark_primaryContainer,
onPrimaryContainer = md_theme_dark_onPrimaryContainer,
secondary = md_theme_dark_secondary,
onSecondary = md_theme_dark_onSecondary,
secondaryContainer = md_theme_dark_secondaryContainer,
onSecondaryContainer = md_theme_dark_onSecondaryContainer,
tertiary = md_theme_dark_tertiary,
onTertiary = md_theme_dark_onTertiary,
tertiaryContainer = md_theme_dark_tertiaryContainer,
onTertiaryContainer = md_theme_dark_onTertiaryContainer,
error = md_theme_dark_error,
errorContainer = md_theme_dark_errorContainer,
onError = md_theme_dark_onError,
onErrorContainer = md_theme_dark_onErrorContainer,
background = md_theme_dark_background,
onBackground = md_theme_dark_onBackground,
surface = md_theme_dark_surface,
onSurface = md_theme_dark_onSurface,
surfaceVariant = md_theme_dark_surfaceVariant,
onSurfaceVariant = md_theme_dark_onSurfaceVariant,
outline = md_theme_dark_outline,
inverseOnSurface = md_theme_dark_inverseOnSurface,
inverseSurface = md_theme_dark_inverseSurface,
inversePrimary = md_theme_dark_inversePrimary,
surfaceTint = md_theme_dark_surfaceTint,
outlineVariant = md_theme_dark_outlineVariant,
scrim = md_theme_dark_scrim,
)
same with the Color.kt file
val md_theme_light_primary = Color(0xFFA43D00)
val md_theme_light_onPrimary = Color(0xFFFFFFFF)
val md_theme_light_primaryContainer = Color(0xFFFFDBCD)
val md_theme_light_onPrimaryContainer = Color(0xFF360F00)
val md_theme_light_secondary = Color(0xFF006782)
val md_theme_light_onSecondary = Color(0xFFFFFFFF)
val md_theme_light_secondaryContainer = Color(0xFFBBEAFF)
val md_theme_light_onSecondaryContainer = Color(0xFF001F29)
val md_theme_light_tertiary = Color(0xFF006B5A)
val md_theme_light_onTertiary = Color(0xFFFFFFFF)
val md_theme_light_tertiaryContainer = Color(0xFF79F8DC)
val md_theme_light_onTertiaryContainer = Color(0xFF00201A)
val md_theme_light_error = Color(0xFFBA1A1A)
val md_theme_light_errorContainer = Color(0xFFFFDAD6)
val md_theme_light_onError = Color(0xFFFFFFFF)
val md_theme_light_onErrorContainer = Color(0xFF410002)
val md_theme_light_background = Color(0xFFFFFBFF)
val md_theme_light_onBackground = Color(0xFF221B00)
val md_theme_light_surface = Color(0xFFFFFBFF)
val md_theme_light_onSurface = Color(0xFF221B00)
val md_theme_light_surfaceVariant = Color(0xFFF5DED6)
val md_theme_light_onSurfaceVariant = Color(0xFF53433E)
val md_theme_light_outline = Color(0xFF85736D)
val md_theme_light_inverseOnSurface = Color(0xFFFFF0C0)
val md_theme_light_inverseSurface = Color(0xFF3A3000)
val md_theme_light_inversePrimary = Color(0xFFFFB597)
val md_theme_light_shadow = Color(0xFF000000)
val md_theme_light_surfaceTint = Color(0xFFA43D00)
val md_theme_light_outlineVariant = Color(0xFFD8C2BA)
val md_theme_light_scrim = Color(0xFF000000)
val md_theme_dark_primary = Color(0xFFFFB597)
val md_theme_dark_onPrimary = Color(0xFF591D00)
val md_theme_dark_primaryContainer = Color(0xFF7D2D00)
val md_theme_dark_onPrimaryContainer = Color(0xFFFFDBCD)
val md_theme_dark_secondary = Color(0xFF60D4FE)
val md_theme_dark_onSecondary = Color(0xFF003545)
val md_theme_dark_secondaryContainer = Color(0xFF004D62)
val md_theme_dark_onSecondaryContainer = Color(0xFFBBEAFF)
val md_theme_dark_tertiary = Color(0xFF5ADBC0)
val md_theme_dark_onTertiary = Color(0xFF00382E)
val md_theme_dark_tertiaryContainer = Color(0xFF005144)
val md_theme_dark_onTertiaryContainer = Color(0xFF79F8DC)
val md_theme_dark_error = Color(0xFFFFB4AB)
val md_theme_dark_errorContainer = Color(0xFF93000A)
val md_theme_dark_onError = Color(0xFF690005)
val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6)
val md_theme_dark_background = Color(0xFF221B00)
val md_theme_dark_onBackground = Color(0xFFFFE264)
val md_theme_dark_surface = Color(0xFF221B00)
val md_theme_dark_onSurface = Color(0xFFFFE264)
val md_theme_dark_surfaceVariant = Color(0xFF53433E)
val md_theme_dark_onSurfaceVariant = Color(0xFFD8C2BA)
val md_theme_dark_outline = Color(0xFFA08D86)
val md_theme_dark_inverseOnSurface = Color(0xFF221B00)
val md_theme_dark_inverseSurface = Color(0xFFFFE264)
val md_theme_dark_inversePrimary = Color(0xFFA43D00)
val md_theme_dark_shadow = Color(0xFF000000)
val md_theme_dark_surfaceTint = Color(0xFFFFB597)
val md_theme_dark_outlineVariant = Color(0xFF53433E)
val md_theme_dark_scrim = Color(0xFF000000)
val seed = Color(0xFFF26419)
val Olivine = Color(0xFFA1C182)
val Sunglow = Color(0xFFFFCB3D)
val light_Olivine = Color(0xFF3F6918)
val light_onOlivine = Color(0xFFFFFFFF)
val light_OlivineContainer = Color(0xFFBFF290)
val light_onOlivineContainer = Color(0xFF0D2000)
val dark_Olivine = Color(0xFFA4D577)
val dark_onOlivine = Color(0xFF1A3700)
val dark_OlivineContainer = Color(0xFF295000)
val dark_onOlivineContainer = Color(0xFFBFF290)
val light_Sunglow = Color(0xFF775A00)
val light_onSunglow = Color(0xFFFFFFFF)
val light_SunglowContainer = Color(0xFFFFDF97)
val light_onSunglowContainer = Color(0xFF251A00)
val dark_Sunglow = Color(0xFFF2C031)
val dark_onSunglow = Color(0xFF3E2E00)
val dark_SunglowContainer = Color(0xFF5A4400)
val dark_onSunglowContainer = Color(0xFFFFDF97)
Just replaced the old color information with the new one. It feels like this is not the right way to do it though... Thank you in advanced for your help.
Please check this answer. Same case.
You can remove the code responsible for dynamic colors in your theme.kt file.
To know more about Dynamic Colors, please refer this documentation page.
val darkTheme = isSystemInDarkTheme()
val dynamicColor = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
val colors = when {
// dynamicColor && darkTheme - > dynamicDarkColorScheme(LocalContext.current)
// dynamicColor && !darkTheme - > dynamicLightColorScheme(LocalContext.current)
darkTheme - > darkColorScheme
else - > lightColorScheme
}
Sup, guys! I have a task to calculate coordinates of continuation line between points A & B. I have a code each calculate distance between these points, and I have tangent of angle for calculating next points D and F. Thanks for any help
`val pointA = LatLng(a.latitude, a.longitude)
val pointB = LatLng(b.latitude, b.longitude)
val pointC = LatLng(a.latitude, b.longitude)//for calculating
val polylineOptions = PolylineOptions()
.add(pointA)
.add(pointB)
.add(pointC)
.add(pointA)
val distanceAB = getKmFromLatLong(pointA.latitude, pointA.longitude, pointB.latitude, pointB.longitude)
val distanceBC = getKmFromLatLong(pointB.latitude, pointB.longitude, pointC.latitude, pointC.longitude)
val distanceCA = getKmFromLatLong(pointC.latitude, pointC.longitude, pointA.latitude, pointA.longitude)
val tang = distanceBC/distanceCA
println("__ distanceAB $distanceAB")
println("__ distanceBC $distanceBC")
println("__ distanceCA $distanceCA")
println("__ tang $tang")`
Thanks, guys! Point-slope formula - rules
private fun generateNeedingPoints(a: LatLng, b: LatLng): List<LatLng> {
setScreenBounds()
val c: LatLng
val d: LatLng
val longitudeC = if (a.longitude < b.longitude) northEast.longitude else southWest.longitude
val longitudeD = if (a.longitude < b.longitude) southWest.longitude else northEast.longitude
c = calculateEdgeButton(a, b, longitudeC)
d = calculateEdgeButton(b, a, longitudeD)
return mutableListOf(d, a, b, c)
}
`
private fun calculateEdgeButton(a: LatLng, b: LatLng, edgeLng: Double, distortionCoefficient:Double = 1.0): LatLng{
val longChange = b.longitude - a.longitude
val latChange = b.latitude - a.latitude
val slope = latChange / longChange
val longChangePointC = edgeLng - b.longitude
val latChangePointC = longChangePointC * slope
val latitudeC = b.latitude + latChangePointC
return LatLng(latitudeC * distortionCoefficient, edgeLng)
}
`
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 11 months ago.
Improve this question
I have a codebase where all my data calculations and data formatting occur within a single function. I have heard that it is best practice to have one task per function but I wonder if calculations, formatting, and display can fit into one function as data processing? Or should I separate them all?
Here is the function I am referring to:
private fun processData(data: ByteArray) {
progressBar.visibility = GONE
Log.d(TAG, "displayDiagnosticData: ")
val bmsVersionView = findViewById<TextView>(R.id.textview_bms_version)
val boardVersionView = findViewById<TextView>(R.id.textview_board_version)
val cellOneView = findViewById<TextView>(R.id.textview_cell_1)
val cellTwoView = findViewById<TextView>(R.id.textview_cell_2)
val cellThreeView = findViewById<TextView>(R.id.textview_cell_3)
val cellFourView = findViewById<TextView>(R.id.textview_cell_4)
val cellFiveView = findViewById<TextView>(R.id.textview_cell_5)
val cellSixView = findViewById<TextView>(R.id.textview_cell_6)
val cellSevenView = findViewById<TextView>(R.id.textview_cell_7)
val cellEightView = findViewById<TextView>(R.id.textview_cell_8)
val cellNineView = findViewById<TextView>(R.id.textview_cell_9)
val cellTenView = findViewById<TextView>(R.id.textview_cell_10)
val cellElevenView = findViewById<TextView>(R.id.textview_cell_11)
val cellTwelveView = findViewById<TextView>(R.id.textview_cell_12)
val cellThirteenView = findViewById<TextView>(R.id.textview_cell_13)
val cellFourteenView = findViewById<TextView>(R.id.textview_cell_14)
val packTotalView = findViewById<TextView>(R.id.textview_diagnostic_voltage)
val packSocView = findViewById<TextView>(R.id.textview_diagnostic_soc)
val chargeTempView = findViewById<TextView>(R.id.textview_charge_temp)
val dischargeTempView = findViewById<TextView>(R.id.textview_discharge_temp)
val chargeCurrentView = findViewById<TextView>(R.id.textview_diagnostic_charge_current)
// val dischargeCurrentView = findViewById<TextView>(R.id.textview_diagnostic_discharge_current)
val dischargeCircuitStateView = findViewById<TextView>(R.id.textview_discharge_circuit)
val chargeCircuitStateView = findViewById<TextView>(R.id.textview_charge_circuit)
val balanceCircuitStateView = findViewById<TextView>(R.id.textview_balance_circuit)
val emptyCircuitStateView = findViewById<TextView>(R.id.textview_empty_circuit)
val bmsVersion = data[0] + (data[1] * 256)
val cellOne = data[2].toDouble() / 100 + 3.52
val cellTwo = data[3].toDouble() / 100 + 3.52
val cellThree = data[4].toDouble() / 100 + 3.52
val cellFour = data[5].toDouble() / 100 + 3.52
val cellFive = data[6].toDouble() / 100 + 3.52
val cellSix = data[7].toDouble() / 100 + 3.52
val cellSeven = data[8].toDouble() / 100 + 3.52
val cellEight = data[9].toDouble() / 100 + 3.52
val cellNine = data[10].toDouble() / 100 + 3.52
val cellTen = data[11].toDouble() / 100 + 3.52
val cellEleven = data[12].toDouble() / 100 + 3.52
val cellTwelve = data[13].toDouble() / 100 + 3.52
val cellThirteen = data[14].toDouble() / 100 + 3.52
val cellFourteen = data[15].toDouble() / 100 + 3.52
val totalVoltage = 47.8 + (data[16].toDouble() / 10)
val chargeTempCelsius = data[19]
val dischargeTempCelsius = data[20]
val chargeTempFahr = (chargeTempCelsius * 9.0 / 5.0) + 32.0
val dischargeTempFahr = (dischargeTempCelsius * 9.0 / 5.0) + 32.0
val chargeCurrent = data[21]
// val dischargeCurrent = (data[23].toDouble() * 100 + data[22]).toInt()
val chargeCircuitState = data[25].toInt()
val dischargeCircuitState = data[26].toInt()
val balanceCircuitState = data[27].toInt()
val emptyCircuitState = data[28].toInt()
val chargeCircuit: String = if (chargeCircuitState == 1) {
"On"
}else {
"Off"
}
val dischargeCircuit: String = if (dischargeCircuitState == 1) {
"On"
}else {
"Off"
}
val balanceCircuit: String = if (balanceCircuitState == 1) {
"On"
}else {
"Off"
}
val emptyCircuit: String = if (emptyCircuitState == 1) {
"On"
}else {
"Off"
}
val bmsVersionString = SpannableString("BMS Version: $bmsVersion")
bmsVersionString.setSpan(StyleSpan(Typeface.BOLD), 0, 11, 0)
val boardVersionString = SpannableString("Board Version: 2.1")
boardVersionString.setSpan(StyleSpan(Typeface.BOLD), 0, 13, 0)
val cellOneString = SpannableString("Cell 1: %.2fV".format(cellOne))
cellOneString.setSpan(StyleSpan(Typeface.BOLD), 0, 6, 0)
val cellTwoString = SpannableString("Cell 2: %.2fV".format(cellTwo))
cellTwoString.setSpan(StyleSpan(Typeface.BOLD), 0, 6, 0)
val cellThreeString = SpannableString("Cell 3: %.2fV".format(cellThree))
cellThreeString.setSpan(StyleSpan(Typeface.BOLD), 0, 6, 0)
val cellFourString = SpannableString("Cell 4: %.2fV".format(cellFour))
cellFourString.setSpan(StyleSpan(Typeface.BOLD), 0, 6, 0)
val cellFiveString = SpannableString("Cell 5: %.2fV".format(cellFive))
cellFiveString.setSpan(StyleSpan(Typeface.BOLD), 0, 6, 0)
val cellSixString = SpannableString("Cell 6: %.2fV".format(cellSix))
cellSixString.setSpan(StyleSpan(Typeface.BOLD), 0, 6, 0)
val cellSevenString = SpannableString("Cell 7: %.2fV".format(cellSeven))
cellSevenString.setSpan(StyleSpan(Typeface.BOLD), 0, 6, 0)
val cellEightString = SpannableString("Cell 8: %.2fV".format(cellEight))
cellEightString.setSpan(StyleSpan(Typeface.BOLD), 0, 6, 0)
val cellNineString = SpannableString("Cell 9: %.2fV".format(cellNine))
cellNineString.setSpan(StyleSpan(Typeface.BOLD), 0, 6, 0)
val cellTenString = SpannableString("Cell 10: %.2fV".format(cellTen))
cellTenString.setSpan(StyleSpan(Typeface.BOLD), 0, 7, 0)
val cellElevenString = SpannableString("Cell 11: %.2fV".format(cellEleven))
cellElevenString.setSpan(StyleSpan(Typeface.BOLD), 0, 7, 0)
val cellTwelveString = SpannableString("Cell 12: %.2fV".format(cellTwelve))
cellTwelveString.setSpan(StyleSpan(Typeface.BOLD), 0, 7, 0)
val cellThirteenString = SpannableString("Cell 13: %.2fV".format(cellThirteen))
cellThirteenString.setSpan(StyleSpan(Typeface.BOLD), 0, 7, 0)
val cellFourteenString = SpannableString("Cell 14: %.2fV".format(cellFourteen))
cellFourteenString.setSpan(StyleSpan(Typeface.BOLD), 0, 7, 0)
val packTotalString = SpannableString("Pack Total: %.1fV".format(totalVoltage))
packTotalString.setSpan(StyleSpan(Typeface.BOLD), 0, 10, 0)
val socString = SpannableString("SOC: ${data[17].toInt()}%")
socString.setSpan(StyleSpan(Typeface.BOLD), 0, 3, 0)
val chargeTempString = SpannableString("Charge Temp: ${chargeTempFahr.toInt()}" + "°F")
chargeTempString.setSpan(StyleSpan(Typeface.BOLD), 0, 11, 0)
val dischargeTempString = SpannableString("Discharge Temp: ${dischargeTempFahr.toInt()}" + "°F")
dischargeTempString.setSpan(StyleSpan(Typeface.BOLD), 0, 15, 0)
val chargeCurrentString = SpannableString("Charge Current: $chargeCurrent" + "A")
chargeCurrentString.setSpan(StyleSpan(Typeface.BOLD), 0, 14, 0)
// val dischargeCurrentString = "Discharge Current: $dischargeCurrent" + "A"
val chargeCircuitStateString = SpannableString("Charge Circuit: $chargeCircuit")
chargeCircuitStateString.setSpan(StyleSpan(Typeface.BOLD), 0, 14, 0)
val dischargeCircuitStateString = SpannableString("Discharge Circuit: $dischargeCircuit")
dischargeCircuitStateString.setSpan(StyleSpan(Typeface.BOLD), 0, 17, 0)
val balanceCircuitStateString = SpannableString("Balance Circuit: $balanceCircuit")
balanceCircuitStateString.setSpan(StyleSpan(Typeface.BOLD), 0, 15, 0)
val emptyCircuitStateString = SpannableString("Empty Circuit: $emptyCircuit")
emptyCircuitStateString.setSpan(StyleSpan(Typeface.BOLD), 0, 13, 0)
bmsVersionView.text = bmsVersionString
boardVersionView.text = boardVersionString
cellOneView.text = cellOneString
cellTwoView.text = cellTwoString
cellThreeView.text = cellThreeString
cellFourView.text = cellFourString
cellFiveView.text = cellFiveString
cellSixView.text = cellSixString
cellSevenView.text = cellSevenString
cellEightView.text = cellEightString
cellNineView.text = cellNineString
cellTenView.text = cellTenString
cellElevenView.text = cellElevenString
cellTwelveView.text = cellTwelveString
cellThirteenView.text = cellThirteenString
cellFourteenView.text = cellFourteenString
packTotalView.text = packTotalString
packSocView.text = socString
chargeTempView.text = chargeTempString
dischargeTempView.text = dischargeTempString
chargeCurrentView.text = chargeCurrentString
// dischargeCurrentView.text = dischargeCurrentString
chargeCircuitStateView.text = chargeCircuitStateString
dischargeCircuitStateView.text = dischargeCircuitStateString
balanceCircuitStateView.text = balanceCircuitStateString
emptyCircuitStateView.text = emptyCircuitStateString
}
Can I keep it as is or would it be better to seperate it out for readability?
When you find yourself copy-pasting code, always stop and consider whether you can solve the same task by iterating or creating a function.
You are doing the same thing to every String, making the text up to the : bold, so you can write a function for that and eliminate a bunch of code:
private fun String.withStyling() = SpannableString(this).apply {
setSpan(StyleSpan(Typeface.BOLD), 0, indexOfFirst(':'), 0)
}
// and then for example:
bmsVersionView.text = "BMS Version: $bmsVersion".withStyling()
boardVersionView.text = "Board Version: 2.1".withStyling()
cellOneView.text = "Cell 1: %.2fV".format(cellOne).withStyling()
// ...
Also, you are doing the exact same thing with every cell view. So make a list of them and iterate!
val cellViews = listOf(
cellOneView,
cellTwoView,
//...
)
for ((i, cellView) in cellViews.withIndex()) {
val value = data[2 + i].toDouble() / 100 + 3.52
val cellNumberString = (i + 1).toString()
cellValue.text = "Cell $cellNumberString: %.2fV".format(value).withStyling()
}
First of all, I will strongly suggest using view & data binding. It will reduce the findviewById lines; it's 2022, and you must change this behavior.
Secondly, separate the code into multiple functions. Otherwise, it will produce a code smell.
I have a codebase where all my data calculations and data formatting occur within a single function. Are there any performance benefits to separating these out into separate functions or does separating them only improve readability?
I know that I should separate them but I don't really know all the reasons why.
Here is the function I am referring to:
private fun processData(data: ByteArray) {
progressBar.visibility = GONE
Log.d(TAG, "displayDiagnosticData: ")
val bmsVersionView = findViewById<TextView>(R.id.textview_bms_version)
val boardVersionView = findViewById<TextView>(R.id.textview_board_version)
val cellOneView = findViewById<TextView>(R.id.textview_cell_1)
val cellTwoView = findViewById<TextView>(R.id.textview_cell_2)
val cellThreeView = findViewById<TextView>(R.id.textview_cell_3)
val cellFourView = findViewById<TextView>(R.id.textview_cell_4)
val cellFiveView = findViewById<TextView>(R.id.textview_cell_5)
val cellSixView = findViewById<TextView>(R.id.textview_cell_6)
val cellSevenView = findViewById<TextView>(R.id.textview_cell_7)
val cellEightView = findViewById<TextView>(R.id.textview_cell_8)
val cellNineView = findViewById<TextView>(R.id.textview_cell_9)
val cellTenView = findViewById<TextView>(R.id.textview_cell_10)
val cellElevenView = findViewById<TextView>(R.id.textview_cell_11)
val cellTwelveView = findViewById<TextView>(R.id.textview_cell_12)
val cellThirteenView = findViewById<TextView>(R.id.textview_cell_13)
val cellFourteenView = findViewById<TextView>(R.id.textview_cell_14)
val packTotalView = findViewById<TextView>(R.id.textview_diagnostic_voltage)
val packSocView = findViewById<TextView>(R.id.textview_diagnostic_soc)
val chargeTempView = findViewById<TextView>(R.id.textview_charge_temp)
val dischargeTempView = findViewById<TextView>(R.id.textview_discharge_temp)
val chargeCurrentView = findViewById<TextView>(R.id.textview_diagnostic_charge_current)
// val dischargeCurrentView = findViewById<TextView>(R.id.textview_diagnostic_discharge_current)
val dischargeCircuitStateView = findViewById<TextView>(R.id.textview_discharge_circuit)
val chargeCircuitStateView = findViewById<TextView>(R.id.textview_charge_circuit)
val balanceCircuitStateView = findViewById<TextView>(R.id.textview_balance_circuit)
val emptyCircuitStateView = findViewById<TextView>(R.id.textview_empty_circuit)
val bmsVersion = data[0] + (data[1] * 256)
val cellOne = data[2].toDouble() / 100 + 3.52
val cellTwo = data[3].toDouble() / 100 + 3.52
val cellThree = data[4].toDouble() / 100 + 3.52
val cellFour = data[5].toDouble() / 100 + 3.52
val cellFive = data[6].toDouble() / 100 + 3.52
val cellSix = data[7].toDouble() / 100 + 3.52
val cellSeven = data[8].toDouble() / 100 + 3.52
val cellEight = data[9].toDouble() / 100 + 3.52
val cellNine = data[10].toDouble() / 100 + 3.52
val cellTen = data[11].toDouble() / 100 + 3.52
val cellEleven = data[12].toDouble() / 100 + 3.52
val cellTwelve = data[13].toDouble() / 100 + 3.52
val cellThirteen = data[14].toDouble() / 100 + 3.52
val cellFourteen = data[15].toDouble() / 100 + 3.52
val totalVoltage = 47.8 + (data[16].toDouble() / 10)
val chargeTempCelsius = data[19]
val dischargeTempCelsius = data[20]
val chargeTempFahr = (chargeTempCelsius * 9.0 / 5.0) + 32.0
val dischargeTempFahr = (dischargeTempCelsius * 9.0 / 5.0) + 32.0
val chargeCurrent = data[21]
// val dischargeCurrent = (data[23].toDouble() * 100 + data[22]).toInt()
val chargeCircuitState = data[25].toInt()
val dischargeCircuitState = data[26].toInt()
val balanceCircuitState = data[27].toInt()
val emptyCircuitState = data[28].toInt()
val chargeCircuit: String = if (chargeCircuitState == 1) {
"On"
}else {
"Off"
}
val dischargeCircuit: String = if (dischargeCircuitState == 1) {
"On"
}else {
"Off"
}
val balanceCircuit: String = if (balanceCircuitState == 1) {
"On"
}else {
"Off"
}
val emptyCircuit: String = if (emptyCircuitState == 1) {
"On"
}else {
"Off"
}
val bmsVersionString = SpannableString("BMS Version: $bmsVersion")
bmsVersionString.setSpan(StyleSpan(Typeface.BOLD), 0, 11, 0)
val boardVersionString = SpannableString("Board Version: 2.1")
boardVersionString.setSpan(StyleSpan(Typeface.BOLD), 0, 13, 0)
val cellOneString = SpannableString("Cell 1: %.2fV".format(cellOne))
cellOneString.setSpan(StyleSpan(Typeface.BOLD), 0, 6, 0)
val cellTwoString = SpannableString("Cell 2: %.2fV".format(cellTwo))
cellTwoString.setSpan(StyleSpan(Typeface.BOLD), 0, 6, 0)
val cellThreeString = SpannableString("Cell 3: %.2fV".format(cellThree))
cellThreeString.setSpan(StyleSpan(Typeface.BOLD), 0, 6, 0)
val cellFourString = SpannableString("Cell 4: %.2fV".format(cellFour))
cellFourString.setSpan(StyleSpan(Typeface.BOLD), 0, 6, 0)
val cellFiveString = SpannableString("Cell 5: %.2fV".format(cellFive))
cellFiveString.setSpan(StyleSpan(Typeface.BOLD), 0, 6, 0)
val cellSixString = SpannableString("Cell 6: %.2fV".format(cellSix))
cellSixString.setSpan(StyleSpan(Typeface.BOLD), 0, 6, 0)
val cellSevenString = SpannableString("Cell 7: %.2fV".format(cellSeven))
cellSevenString.setSpan(StyleSpan(Typeface.BOLD), 0, 6, 0)
val cellEightString = SpannableString("Cell 8: %.2fV".format(cellEight))
cellEightString.setSpan(StyleSpan(Typeface.BOLD), 0, 6, 0)
val cellNineString = SpannableString("Cell 9: %.2fV".format(cellNine))
cellNineString.setSpan(StyleSpan(Typeface.BOLD), 0, 6, 0)
val cellTenString = SpannableString("Cell 10: %.2fV".format(cellTen))
cellTenString.setSpan(StyleSpan(Typeface.BOLD), 0, 7, 0)
val cellElevenString = SpannableString("Cell 11: %.2fV".format(cellEleven))
cellElevenString.setSpan(StyleSpan(Typeface.BOLD), 0, 7, 0)
val cellTwelveString = SpannableString("Cell 12: %.2fV".format(cellTwelve))
cellTwelveString.setSpan(StyleSpan(Typeface.BOLD), 0, 7, 0)
val cellThirteenString = SpannableString("Cell 13: %.2fV".format(cellThirteen))
cellThirteenString.setSpan(StyleSpan(Typeface.BOLD), 0, 7, 0)
val cellFourteenString = SpannableString("Cell 14: %.2fV".format(cellFourteen))
cellFourteenString.setSpan(StyleSpan(Typeface.BOLD), 0, 7, 0)
val packTotalString = SpannableString("Pack Total: %.1fV".format(totalVoltage))
packTotalString.setSpan(StyleSpan(Typeface.BOLD), 0, 10, 0)
val socString = SpannableString("SOC: ${data[17].toInt()}%")
socString.setSpan(StyleSpan(Typeface.BOLD), 0, 3, 0)
val chargeTempString = SpannableString("Charge Temp: ${chargeTempFahr.toInt()}" + "°F")
chargeTempString.setSpan(StyleSpan(Typeface.BOLD), 0, 11, 0)
val dischargeTempString = SpannableString("Discharge Temp: ${dischargeTempFahr.toInt()}" + "°F")
dischargeTempString.setSpan(StyleSpan(Typeface.BOLD), 0, 15, 0)
val chargeCurrentString = SpannableString("Charge Current: $chargeCurrent" + "A")
chargeCurrentString.setSpan(StyleSpan(Typeface.BOLD), 0, 14, 0)
// val dischargeCurrentString = "Discharge Current: $dischargeCurrent" + "A"
val chargeCircuitStateString = SpannableString("Charge Circuit: $chargeCircuit")
chargeCircuitStateString.setSpan(StyleSpan(Typeface.BOLD), 0, 14, 0)
val dischargeCircuitStateString = SpannableString("Discharge Circuit: $dischargeCircuit")
dischargeCircuitStateString.setSpan(StyleSpan(Typeface.BOLD), 0, 17, 0)
val balanceCircuitStateString = SpannableString("Balance Circuit: $balanceCircuit")
balanceCircuitStateString.setSpan(StyleSpan(Typeface.BOLD), 0, 15, 0)
val emptyCircuitStateString = SpannableString("Empty Circuit: $emptyCircuit")
emptyCircuitStateString.setSpan(StyleSpan(Typeface.BOLD), 0, 13, 0)
bmsVersionView.text = bmsVersionString
boardVersionView.text = boardVersionString
cellOneView.text = cellOneString
cellTwoView.text = cellTwoString
cellThreeView.text = cellThreeString
cellFourView.text = cellFourString
cellFiveView.text = cellFiveString
cellSixView.text = cellSixString
cellSevenView.text = cellSevenString
cellEightView.text = cellEightString
cellNineView.text = cellNineString
cellTenView.text = cellTenString
cellElevenView.text = cellElevenString
cellTwelveView.text = cellTwelveString
cellThirteenView.text = cellThirteenString
cellFourteenView.text = cellFourteenString
packTotalView.text = packTotalString
packSocView.text = socString
chargeTempView.text = chargeTempString
dischargeTempView.text = dischargeTempString
chargeCurrentView.text = chargeCurrentString
// dischargeCurrentView.text = dischargeCurrentString
chargeCircuitStateView.text = chargeCircuitStateString
dischargeCircuitStateView.text = dischargeCircuitStateString
balanceCircuitStateView.text = balanceCircuitStateString
emptyCircuitStateView.text = emptyCircuitStateString
}
Performance? No. Maintainability? Yes. Separation of concerns is one of the core tenets of modern software architecture. Combining the two things makes a difficult to read, difficult to debug mess. Separating them allows you to concentrate on one thing at a time, and makes it easier for people maintaining the code (which may even be you 6 months from now when fixing a bug and you've forgotten how it works) to understand the logic and flow of the program.
That function you posted would not be accepted in any professional codebase. It's too long, it does too many things. It needs to be broken up.
Your bigger problem there is that you're repeating yourself, a lot. Not only does that make the whole thing longer, and arguably harder to read, it also makes it hard to maintain like Gabe says, and makes it far more likely a bug will sneak in there. Imagine you needed to add another row of cells - there's a lot of boilerplate involved, a lot of repetitive work, and that's where humans tend to mess up
Like as an example of the kind of thing you can do - see how your cell data is basically taken from a range of values in data, with the same calculation applied to each? You could do this instead:
val cells = (2..15).map { index -> data[index].toDouble() / 100 + 3.52 }
or, to keep things more explicit and separate:
// Except give this a good name since it's doing something specific
// Because this is some kind of conversion formula, putting it in its own function
// makes it easy to maintain and document, and it's clear exactly what it is
fun getCellValue(dataValue: Int) = dataValue.toDouble() / 100 + 3.52
val cells = (2..15).map { index -> getCellValue(data[index]) }
Now you have one or two lines replacing 14 lines of initialisation code. It's also easier to make changes - if the format of data changes, you can easily change the range of indices to use, or the formula applied to each value. It's one place, instead of on each line, where you have to update each one and make sure you haven't made a typo or skipped one.
And when you have structured data like that, it can make your other code easier to work with too - because instead of needing to work with separate variables, you can work with indices and loop over things instead of writing each step out:
// no need for a separate line for each with hardcoded values if you can work it out
// (Because it's a separate function, you can use it for the other display lines too,
// it's not cell-specific)
fun SpannableString.applyBoldPrefix() = apply {
val colonIndex = indexOf(':')
if (colonIndex >= 0) setSpan(StyleSpan(Typeface.BOLD), 0, colonIndex, 0)
}
// you could also just pass in the index and look up cells[index] here -
// this is a more functional approach, but whatever's good
fun getCellDisplayString(cellIndex: Int, cellData: Double) =
SpannableString("Cell ${cellIndex + 1}: %.2fV".format(cellData))
.applyBoldPrefix()
// lots of ways to define/grab a set of views programmatically - putting them all
// in a container you can look up is one way. You can also generate resource identifier
// strings, like "R.id.textview_cell_$i" and look that up
val cellTextViews = findViewById<ViewGroup>(R.id.containerHoldingCells)
.children.filterIsInstance<TextView>
// now you can just iterate over stuff to complete the repetitive task
cellTextViews.forEachIndexed { i, view ->
view.text = getCellDisplayString(i, cells[i])
}
That's about half your code right there. I wouldn't necessarily structure everything this way (I feel like given you're working with a data format here, a more formal structure definition would be helpful, and you can generalise more too) and it's a bit rough-and-ready, but hopefully it gives you a general sense of how you can cut things down, but also make it easier to maintain them and try out changes
I condensed my code a lot more and created functions to process separate chunks of data. I also was able to get rid of a lot of my repetitious code. For anyone who is interested, here is the updated code:
private fun String.withStyling() = SpannableString(this).apply {
setSpan(StyleSpan(Typeface.BOLD), 0, indexOfFirst { it == ':' }, 0)
}
private fun processDiagnosticData(data: ByteArray) {
binding.progressBarCyclic.visibility = GONE
Log.d(TAG, "displayDiagnosticData: ")
processCells(data)
processTemps(data[19], data[20])
processCircuits(data)
processOtherData(data)
}
// Process cells 1-14 and display.
private fun processCells(data: ByteArray) {
val cellViews = listOf(
binding.textviewCell1,
binding.textviewCell2,
binding.textviewCell3,
binding.textviewCell4,
binding.textviewCell5,
binding.textviewCell6,
binding.textviewCell7,
binding.textviewCell8,
binding.textviewCell9,
binding.textviewCell10,
binding.textviewCell11,
binding.textviewCell12,
binding.textviewCell13,
binding.textviewCell14
)
for ((i, cellView) in cellViews.withIndex()) {
val value = data[2 + i].toDouble() / 100 + 3.52
val cellNumberString = (i + 1).toString()
val formattedString = "Cell $cellNumberString: %.2fV".format(value).withStyling()
cellView.text = formattedString
}
}
// Process charge/discharge temps and display.
private fun processTemps(chargeTempCel: Byte, dischargeTempCel: Byte) {
val chargeTempFahr = chargeTempCel * 9.0 / 5.0 + 32.0
val dischargeTempFahr = dischargeTempCel * 9.0 / 5.0 + 32.0
val chargeTempString = "Charge Temp: $chargeTempFahr°F".withStyling()
val dischargeTempString = "Discharge Temp: $dischargeTempFahr°F".withStyling()
binding.textviewChargeTemp.text = chargeTempString
binding.textviewDischargeTemp.text = dischargeTempString
}
// Process circuit states and display.
private fun processCircuits(data: ByteArray) {
val circuitViews = listOf(
binding.textviewChargeCircuit,
binding.textviewDischargeCircuit,
binding.textviewBalanceCircuit,
binding.textviewEmptyCircuit
)
val circuitNames = listOf(
"Charge Circuit: ",
"Discharge Circuit: ",
"Balance Circuit: ",
"Empty Circuit: "
)
for ((i, circuit) in circuitViews.withIndex()) {
val value = if (data[25 + i].toInt() == 1) {
"On"
} else {
"Off"
}
val formattedString = (circuitNames[i] + value).withStyling()
circuit.text = formattedString
}
}
// Process the rest of the data and display.
private fun processOtherData(data: ByteArray) {
val totalVoltage = 47.8 + (data[16].toDouble() / 10)
val packSoc = data[17].toInt()
val chargeCurrent = data[21]
// val dischargeCurrent = (data[23].toDouble() * 100 + data[22]).toInt()
val bmsVersionString = "BMS Version: ${data[0] + (data[1] * 256)}".withStyling()
val boardVersionString = "Board Version: 2.1".withStyling()
val totalVoltageString = "Pack Total: %.1fV".format(totalVoltage).withStyling()
val packSocString = "SOC: ${packSoc}%".withStyling()
val chargeCurrentString = "Charge Current: ${chargeCurrent}A".withStyling()
// val dischargeCurrent = "Discharge Current: $dischargeCurrentA".withStyling()
binding.textviewBmsVersion.text = bmsVersionString
binding.textviewBoardVersion.text = boardVersionString
binding.textviewDiagnosticVoltage.text = totalVoltageString
binding.textviewDiagnosticSoc.text = packSocString
binding.textviewDiagnosticChargeCurrent.text = chargeCurrentString
// binding.textviewDiagnosticDischargeCurrent.text = dischargeCurrentString
}
The below class takes list of data from Firebase Firestore,
then I pass one of this data to another function to get data from Firestore.
The problem is that after the sheet created the data from the list appeared in the sheet
but the data from the gettingData() appear with initializing values.
I mean siteName appears as "Name" in the sheet, not the value from database.
class SaveSitesToExcel {
val dataStore by lazy {
FirebaseFirestore.getInstance()
}
var siteArea = "Area"
var siteName = "Name"
var siteArabicName = "Arabic Name"
var siteOffice = "Office"
var siteRegion = "Region"
fun saveExcelFile(List : List<Sites>, context: Context, fileName: String) {
// check if available and not read only
//New Workbook
val wb = HSSFWorkbook()
lateinit var c: Cell
//Cell style for header row
val cs = wb.createCellStyle()
cs.setFillForegroundColor(HSSFColor.GREY_25_PERCENT.index)
cs.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND)
val cs1 = wb.createCellStyle()
cs1.setFillForegroundColor(HSSFColor.BLUE.index)
cs1.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND)
val cs2 = wb.createCellStyle()
cs2.setFillForegroundColor(HSSFColor.WHITE.index)
cs2.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND)
//New Sheet
lateinit var sheet1: Sheet
sheet1 = wb.createSheet("Sites")
val row = sheet1.createRow(0)
c = row.createCell(0)
c.setCellValue("Area")
c.setCellStyle(cs)
c = row.createCell(1)
c.setCellValue("Site Code")
c.setCellStyle(cs)
c = row.createCell(2)
c.setCellValue("Site Name")
c.setCellStyle(cs)
c = row.createCell(3)
c.setCellValue("Arabic Name")
c.setCellStyle(cs)
c = row.createCell(4)
c.setCellValue("Region")
c.setCellStyle(cs)
c = row.createCell(5)
c.setCellValue("Office")
c.setCellStyle(cs)
c = row.createCell(6)
c.setCellValue("Date")
c.setCellStyle(cs1)
c = row.createCell(7)
c.setCellValue("Guard Quantity")
c.setCellStyle(cs1)
c = row.createCell(8)
c.setCellValue("Quantity Before")
c.setCellStyle(cs1)
c = row.createCell(9)
c.setCellValue("Quantity Added")
c.setCellStyle(cs1)
c = row.createCell(10)
c.setCellValue("Total")
c.setCellStyle(cs1)
sheet1.setColumnWidth(0, 15 * 500)
sheet1.setColumnWidth(1, 15 * 500)
sheet1.setColumnWidth(2, 15 * 500)
sheet1.setColumnWidth(3, 15 * 500)
sheet1.setColumnWidth(4, 15 * 500)
sheet1.setColumnWidth(5, 15 * 500)
for (n in 1 until List.size+1){
gettingSiteData(List[n-1].siteCode)
val row = sheet1.createRow(n)
c = row.createCell(0)
c.setCellValue(siteArea)
c.setCellStyle(cs2)
c = row.createCell(1)
c.setCellValue(List[n-1].siteCode)
c.setCellStyle(cs2)
c = row.createCell(2)
c.setCellValue(siteName)
c.setCellStyle(cs2)
c = row.createCell(3)
c.setCellValue(siteArabicName)
c.setCellStyle(cs2)
c = row.createCell(4)
c.setCellValue(siteRegion)
c.setCellStyle(cs2)
c = row.createCell(5)
c.setCellValue(siteOffice)
c.setCellStyle(cs2)
c = row.createCell(6)
c.setCellValue(List[n-1].visitDate)
c.setCellStyle(cs2)
c = row.createCell(7)
c.setCellValue(List[n-1].guardQty)
c.setCellStyle(cs2)
c = row.createCell(8)
c.setCellValue(List[n-1].qtyBefore)
c.setCellStyle(cs2)
c = row.createCell(9)
c.setCellValue(List[n-1].qtyAdded)
c.setCellStyle(cs2)
c = row.createCell(10)
c.setCellValue(List[n-1].qtyAfter)
c.setCellStyle(cs2)
sheet1.setColumnWidth(0, 15 * 500)
sheet1.setColumnWidth(1, 15 * 500)
sheet1.setColumnWidth(2, 15 * 500)
sheet1.setColumnWidth(3, 15 * 500)
sheet1.setColumnWidth(4, 15 * 500)
sheet1.setColumnWidth(5, 15 * 500)
sheet1.setColumnWidth(6, 15 * 500)
sheet1.setColumnWidth(7, 15 * 500)
sheet1.setColumnWidth(8, 15 * 500)
sheet1.setColumnWidth(9, 15 * 500)
sheet1.setColumnWidth(10, 15 * 500)
Toast.makeText(context,siteArabicName,Toast.LENGTH_SHORT).show()
}
// Create a path where we will place our List of objects on external storage
val file = File(context.getExternalFilesDir(null), fileName)
var os = FileOutputStream(file)
wb.write(os)
os.close()
alertDialog("Saved",R.drawable.ic_done,context).createDialog()
}
fun gettingSiteData(siteCode : String){
dataStore.collection("Sites").document(siteCode).get()
.addOnSuccessListener {
var site = it.toObject(SiteData::class.java)
siteArea = site?.area.toString()
siteName = site?.siteStandardName.toString()
siteArabicName = site?.siteArabicName.toString()
siteOffice = site?.Office.toString()
siteRegion = site?.Region.toString()
}
}
}