Skip to content

Commit

Permalink
Prototype evolved. Use new version of Donut Fork with StrokeCap support
Browse files Browse the repository at this point in the history
  • Loading branch information
mtotschnig committed Oct 10, 2024
1 parent 15845a7 commit 556eae9
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 32 deletions.
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ evernote = "1.4.1"
composeBom = "2024.09.03"
dagger = "2.51.1"
datastorePreferences = "1.1.1"
donut = "7b5a9cdd3b"
donut = "c11f2d1aeb"
flexbox = "3.0.0"
fontdrawable = "39e8f63ce2"
fragmentVersion = "1.8.4"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import androidx.compose.ui.semantics.collectionInfo
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastRoundToInt
import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.drawable.DrawableCompat
import androidx.core.view.isVisible
Expand Down Expand Up @@ -2082,7 +2083,7 @@ abstract class BaseMyExpenses : LaunchActivity(), OnDialogResultListener, Contri
}

with(binding.toolbar.progressPercent) {
text = "%d".format(it)
text = "%d".format(it.fastRoundToInt())
setTextColor(this@BaseMyExpenses.getAmountColor(account.criterion?.sign ?: 0))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ fun HeaderRenderer(
)
}
if (budget?.second != null && budget.second != 0L) {
val progress = (-headerRow.expenseSum.amountMinor * 100F / budget.second).roundToInt()
val progress = (-headerRow.expenseSum.amountMinor * 100F / budget.second)
Row(verticalAlignment = Alignment.CenterVertically) {
DonutInABox(
modifier = Modifier
Expand Down Expand Up @@ -550,7 +550,7 @@ private fun Header() {
)
}

@Preview(name = "Tablet", device = TABLET, locale = "en")
@Preview(name = "Tablet", device = "spec:width=1280dp,height=800dp,dpi=240", locale = "en")
@Composable
private fun HeaderWithBudgetProgress() {
val amount = Money(CurrencyUnit.DebugInstance, 1234)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.sp
import androidx.compose.ui.util.fastRoundToInt
import app.futured.donut.compose.DonutProgress
import app.futured.donut.compose.data.DonutModel
import app.futured.donut.compose.data.DonutSection
Expand Down Expand Up @@ -84,7 +86,7 @@ val generalPadding
@Composable
fun DonutInABox(
modifier: Modifier,
progress: Int,
progress: Float,
fontSize: TextUnit,
color: Color,
excessColor: Color
Expand All @@ -98,12 +100,13 @@ fun DonutInABox(
gapWidthDegrees = 0f,
gapAngleDegrees = 0f,
strokeWidth = LocalContext.current.resources.getDimension(R.dimen.progress_donut_stroke_width),
strokeCap = StrokeCap.Butt,
sections = calcProgressVisualRepresentation(progress).forCompose(color, excessColor)
)
)
Text(
modifier = Modifier.align(Alignment.Center),
text = "%d".format(progress),
text = "%d".format(progress.fastRoundToInt()),
fontSize = fontSize,
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,40 +1,107 @@
package org.totschnig.myexpenses.dialog

import androidx.compose.animation.core.CubicBezierEasing
import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import app.futured.donut.compose.DonutProgress
import app.futured.donut.compose.data.DonutConfig
import app.futured.donut.compose.data.DonutModel
import kotlinx.coroutines.delay
import org.totschnig.myexpenses.R
import org.totschnig.myexpenses.dialog.CriterionReachedDialogFragment.Companion.ANIMATION_DURATION
import org.totschnig.myexpenses.dialog.CriterionReachedDialogFragment.Companion.ANIMATION_DURATION_L
import org.totschnig.myexpenses.util.ui.calcProgressVisualRepresentation
import org.totschnig.myexpenses.util.ui.forCompose
import kotlin.math.absoluteValue

data class CriterionReachedEventInfo(
val startBalance: Long,
val criterion: Long,
val transactionAmount: Long
) {
init {
require((startBalance + transactionAmount).absoluteValue >= criterion.absoluteValue)
}

val newBalance = startBalance + transactionAmount

val startProgress = startBalance * 100F / criterion
val endProgress = newBalance * 100f / criterion
}

class CriterionReachedDialogFragment: ComposeBaseDialogFragment3() {
val info = CriterionReachedEventInfo(
startBalance = 65,
criterion = 100,
transactionAmount = 75
)

@Composable
override fun ColumnScope.MainContent() {

Column {
Text("Warning")
DonutProgress(
modifier = Modifier.fillMaxWidth().height(240.dp).width(240.dp),
model = DonutModel(
cap = 100f,
sections = calcProgressVisualRepresentation(120).forCompose(
valueColor = Color.Green, excessColor = Color.Red
)
)
)
Text("You have reached your credit limit")
CriterionReachedGraph(info)
Text("You have reached your credit limit.")
}
}

override val title: CharSequence
get() = getString(R.string.credit_limit)

companion object {
const val ANIMATION_DURATION = 1000
const val ANIMATION_DURATION_L = ANIMATION_DURATION.toLong()
}
}

@Composable
fun CriterionReachedGraph(info: CriterionReachedEventInfo) {
val progress = remember { mutableFloatStateOf(info.startProgress) }
DonutProgress(
modifier = Modifier.height(240.dp).width(240.dp),
model = DonutModel(
cap = 100f,
sections = calcProgressVisualRepresentation(progress.floatValue).forCompose(Color.Green, Color.Red),
gapWidthDegrees = 0f,
gapAngleDegrees = 0f,
strokeCap = StrokeCap.Butt,
backgroundLineColor = Color(0xFFE7E8E9)
),
config = DonutConfig.create(
layoutAnimationSpec = tween(
durationMillis = 1000,
easing = CubicBezierEasing(0.18f, 0.7f, 0.16f, 1f)
),
colorAnimationSpec = tween(durationMillis = ANIMATION_DURATION)
)
)
LaunchedEffect(Unit) {
delay(ANIMATION_DURATION_L)
progress.floatValue = 100F
delay(ANIMATION_DURATION_L)
progress.floatValue = info.endProgress
}
}

@Preview
@Composable
fun Demo() {
CriterionReachedGraph(CriterionReachedEventInfo(
startBalance = 95,
criterion = 100,
transactionAmount = 10
))
}
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ fun attachmentInfoMap(context: Context, withFile: Boolean = false): Map<Uri, Att
"content" -> {
val file = if (withFile) try {
PictureDirHelper.getFileForUri(context, uri)
} catch (e: IllegalArgumentException) {
} catch (_: IllegalArgumentException) {
null
} else null
contentResolver.getType(uri)?.let {
Expand All @@ -267,7 +267,7 @@ fun attachmentInfoMap(context: Context, withFile: Boolean = false): Map<Uri, Att
cancellationSignal
), file
)
} catch (e: Exception) {
} catch (_: Exception) {
null
}
} else {
Expand Down Expand Up @@ -328,28 +328,28 @@ fun Context.getAmountColor(sign: Int) =
if (sign == -1) R.color.colorExpense else R.color.colorIncome,
)

data class DisplayProgress(val displayValue: Int, val displayExcess: Int)
data class DisplayProgress(val displayValue: Float, val displayExcess: Float)

fun DisplayProgress.forViewSystem(valueColor: Int, excessColor: Int) = listOf(
DonutSection("excess", excessColor, displayExcess.toFloat()),
DonutSection("progress", valueColor, displayValue.toFloat())
DonutSection("excess", excessColor, displayExcess),
DonutSection("progress", valueColor, displayValue)
)

fun DisplayProgress.forCompose(
valueColor: androidx.compose.ui.graphics.Color,
excessColor: androidx.compose.ui.graphics.Color,
) = listOf(
app.futured.donut.compose.data.DonutSection(displayExcess.toFloat(), excessColor),
app.futured.donut.compose.data.DonutSection(displayValue.toFloat(), valueColor)
app.futured.donut.compose.data.DonutSection(displayExcess, excessColor),
app.futured.donut.compose.data.DonutSection(displayValue, valueColor)
)

fun calcProgressVisualRepresentation(progress: Int) = when {
fun calcProgressVisualRepresentation(progress: Float) = when {

progress > 200 -> DisplayProgress(0,100)
progress > 200 -> DisplayProgress(0f,100f)

progress > 100 -> DisplayProgress(200 - progress, progress - 100)
progress > 100 -> DisplayProgress(200f - progress, progress - 100)

progress >= 0 -> DisplayProgress(progress, 0)
progress >= 0 -> DisplayProgress(progress, 0f)

else -> throw IllegalArgumentException()
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,11 @@ data class FullAccount(
id, type, sortBy, sortDirection, grouping, currencyUnit, sealed, openingBalance, _color
)

val progress: Int?
val progress: Float?
get() = criterion?.let {
if (it > 0 == currentBalance > 0) {
(currentBalance * 100F / it).roundToInt()
} else 0
(currentBalance * 100F / it)
} else 0f
}

companion object {
Expand Down

0 comments on commit 556eae9

Please sign in to comment.