From 183957c1e806491fc959e8522e2684357cfdae57 Mon Sep 17 00:00:00 2001 From: Atridad Lahiji Date: Sat, 16 Aug 2025 20:20:30 -0600 Subject: [PATCH] 0.4.1 - Small fix for share image --- app/build.gradle.kts | 4 +- .../openclimb/utils/SessionShareUtils.kt | 67 +++++++++++++++++-- 2 files changed, 65 insertions(+), 6 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 24db5fb..5f888d6 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -14,8 +14,8 @@ android { applicationId = "com.atridad.openclimb" minSdk = 31 targetSdk = 35 - versionCode = 7 - versionName = "0.4.0" + versionCode = 8 + versionName = "0.4.1" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } diff --git a/app/src/main/java/com/atridad/openclimb/utils/SessionShareUtils.kt b/app/src/main/java/com/atridad/openclimb/utils/SessionShareUtils.kt index 5305093..098499c 100644 --- a/app/src/main/java/com/atridad/openclimb/utils/SessionShareUtils.kt +++ b/app/src/main/java/com/atridad/openclimb/utils/SessionShareUtils.kt @@ -24,7 +24,8 @@ object SessionShareUtils { val uniqueProblemsCompleted: Int, val averageGrade: String?, val sessionDuration: String, - val topResult: AttemptResult? + val topResult: AttemptResult?, + val topGrade: String? ) fun calculateSessionStats( @@ -58,6 +59,19 @@ object SessionShareUtils { else -> null } + // Determine highest achieved grade (only from completed problems: SUCCESS or FLASH) + val completedProblems = problems.filter { it.id in uniqueCompletedProblems } + val completedBoulder = completedProblems.filter { it.climbType == ClimbType.BOULDER } + val completedRope = completedProblems.filter { it.climbType == ClimbType.ROPE } + val topBoulder = highestGradeForProblems(completedBoulder) + val topRope = highestGradeForProblems(completedRope) + val topGrade = when { + topBoulder != null && topRope != null -> "$topBoulder / $topRope" + topBoulder != null -> topBoulder + topRope != null -> topRope + else -> null + } + val duration = if (session.duration != null) "${session.duration}m" else "Unknown" val topResult = attempts.maxByOrNull { when (it.result) { @@ -76,7 +90,8 @@ object SessionShareUtils { uniqueProblemsCompleted = uniqueCompletedProblems.size, averageGrade = averageGrade, sessionDuration = duration, - topResult = topResult + topResult = topResult, + topGrade = topGrade ) } @@ -249,8 +264,8 @@ object SessionShareUtils { drawStatItem(canvas, width - columnWidth / 2f, rightY, "Completed", stats.uniqueProblemsCompleted.toString(), statLabelPaint, statValuePaint) rightY += 140f - stats.averageGrade?.let { grade -> - drawStatItem(canvas, width - columnWidth / 2f, rightY, "Avg Grade", grade, statLabelPaint, statValuePaint) + stats.topGrade?.let { grade -> + drawStatItem(canvas, width - columnWidth / 2f, rightY, "Top Grade", grade, statLabelPaint, statValuePaint) } // Success rate arc @@ -383,4 +398,48 @@ object SessionShareUtils { e.printStackTrace() } } + + /** + * Returns the highest grade string among the given problems, respecting their difficulty system. + */ + private fun highestGradeForProblems(problems: List): String? { + if (problems.isEmpty()) return null + return problems.maxByOrNull { p -> gradeRank(p.difficulty.system, p.difficulty.grade) }?.difficulty?.grade + } + + /** + * Produces a comparable numeric rank for grades across supported systems. + */ + private fun gradeRank(system: DifficultySystem, grade: String): Double { + return when (system) { + DifficultySystem.V_SCALE -> { + if (grade == "VB") 0.0 else grade.removePrefix("V").toDoubleOrNull() ?: -1.0 + } + DifficultySystem.FONT -> { + val list = DifficultySystem.FONT.getAvailableGrades() + val idx = list.indexOf(grade.uppercase()) + if (idx >= 0) idx.toDouble() else grade.filter { it.isDigit() }.toDoubleOrNull() ?: -1.0 + } + DifficultySystem.YDS -> { + // Parse 5.X with optional letter a-d + val s = grade.lowercase() + if (!s.startsWith("5.")) return -1.0 + val tail = s.removePrefix("5.") + val numberPart = tail.takeWhile { it.isDigit() || it == '.' } + val letterPart = tail.drop(numberPart.length).firstOrNull() + val base = numberPart.toDoubleOrNull() ?: return -1.0 + val letterWeight = when (letterPart) { + 'a' -> 0.0 + 'b' -> 0.1 + 'c' -> 0.2 + 'd' -> 0.3 + else -> 0.0 + } + base + letterWeight + } + DifficultySystem.CUSTOM -> { + grade.filter { it.isDigit() || it == '.' || it == '-' }.toDoubleOrNull() ?: -1.0 + } + } + } }