diff --git a/.gradle/8.11.1/executionHistory/executionHistory.bin b/.gradle/8.11.1/executionHistory/executionHistory.bin index 1c9dc33..695d30c 100644 Binary files a/.gradle/8.11.1/executionHistory/executionHistory.bin and b/.gradle/8.11.1/executionHistory/executionHistory.bin differ diff --git a/.gradle/8.11.1/executionHistory/executionHistory.lock b/.gradle/8.11.1/executionHistory/executionHistory.lock index a24d135..ba4a26b 100644 Binary files a/.gradle/8.11.1/executionHistory/executionHistory.lock and b/.gradle/8.11.1/executionHistory/executionHistory.lock differ diff --git a/.gradle/8.11.1/fileHashes/fileHashes.bin b/.gradle/8.11.1/fileHashes/fileHashes.bin index 33d20bc..95f0b10 100644 Binary files a/.gradle/8.11.1/fileHashes/fileHashes.bin and b/.gradle/8.11.1/fileHashes/fileHashes.bin differ diff --git a/.gradle/8.11.1/fileHashes/fileHashes.lock b/.gradle/8.11.1/fileHashes/fileHashes.lock index 1e836c5..48926bf 100644 Binary files a/.gradle/8.11.1/fileHashes/fileHashes.lock and b/.gradle/8.11.1/fileHashes/fileHashes.lock differ diff --git a/.gradle/8.11.1/fileHashes/resourceHashesCache.bin b/.gradle/8.11.1/fileHashes/resourceHashesCache.bin index 6b243bf..a7ac4a5 100644 Binary files a/.gradle/8.11.1/fileHashes/resourceHashesCache.bin and b/.gradle/8.11.1/fileHashes/resourceHashesCache.bin differ diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock index 4285d3d..53dc418 100644 Binary files a/.gradle/buildOutputCleanup/buildOutputCleanup.lock and b/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/.gradle/file-system.probe b/.gradle/file-system.probe index e40efa0..bd9f815 100644 Binary files a/.gradle/file-system.probe and b/.gradle/file-system.probe differ diff --git a/app/build.gradle.kts b/app/build.gradle.kts index daf76b1..0682845 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -14,15 +14,15 @@ android { applicationId = "com.atridad.openclimb" minSdk = 31 targetSdk = 35 - versionCode = 4 - versionName = "0.3.1" + versionCode = 5 + versionName = "0.3.2" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { - isMinifyEnabled = false + isMinifyEnabled = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" diff --git a/app/src/main/java/com/atridad/openclimb/data/model/ClimbSession.kt b/app/src/main/java/com/atridad/openclimb/data/model/ClimbSession.kt index 7feb45a..87cbe73 100644 --- a/app/src/main/java/com/atridad/openclimb/data/model/ClimbSession.kt +++ b/app/src/main/java/com/atridad/openclimb/data/model/ClimbSession.kt @@ -31,10 +31,10 @@ data class ClimbSession( @PrimaryKey val id: String, val gymId: String, - val date: String, // ISO date string - val startTime: String? = null, // When session was started - val endTime: String? = null, // When session was completed - val duration: Long? = null, // Duration in minutes (calculated when completed) + val date: String, + val startTime: String? = null, + val endTime: String? = null, + val duration: Long? = null, val status: SessionStatus = SessionStatus.ACTIVE, val notes: String? = null, val createdAt: String, diff --git a/app/src/main/java/com/atridad/openclimb/data/model/ClimbType.kt b/app/src/main/java/com/atridad/openclimb/data/model/ClimbType.kt index cfd991d..8647cee 100644 --- a/app/src/main/java/com/atridad/openclimb/data/model/ClimbType.kt +++ b/app/src/main/java/com/atridad/openclimb/data/model/ClimbType.kt @@ -8,7 +8,7 @@ enum class ClimbType { BOULDER; /** - * Get the display name for the UI + * Get the display name */ fun getDisplayName(): String = when (this) { ROPE -> "Rope" diff --git a/app/src/main/java/com/atridad/openclimb/data/model/DifficultySystem.kt b/app/src/main/java/com/atridad/openclimb/data/model/DifficultySystem.kt index e89b87b..eddc028 100644 --- a/app/src/main/java/com/atridad/openclimb/data/model/DifficultySystem.kt +++ b/app/src/main/java/com/atridad/openclimb/data/model/DifficultySystem.kt @@ -4,14 +4,14 @@ import kotlinx.serialization.Serializable @Serializable enum class DifficultySystem { - // Bouldering systems - V_SCALE, // V-Scale (VB - V17) - FONT, // Fontainebleau (3 - 8C+) + // Bouldering + V_SCALE, // V-Scale (VB - V17) + FONT, // Fontainebleau (3 - 8C+) - // Rope climbing systems - YDS, // Yosemite Decimal System (5.0 - 5.15d) + // Rope + YDS, // Yosemite Decimal System (5.0 - 5.15d) - // Custom system for gyms that use their own colors/naming + // Custom difficulty systems CUSTOM; /** @@ -39,22 +39,22 @@ enum class DifficultySystem { fun isRopeSystem(): Boolean = when (this) { YDS -> true V_SCALE, FONT -> false - CUSTOM -> true // Custom is available for all + CUSTOM -> true } /** - * Get available grades for this difficulty system + * Get available grades for this system */ fun getAvailableGrades(): List = when (this) { V_SCALE -> listOf("VB", "V0", "V1", "V2", "V3", "V4", "V5", "V6", "V7", "V8", "V9", "V10", "V11", "V12", "V13", "V14", "V15", "V16", "V17") FONT -> listOf("3", "4A", "4B", "4C", "5A", "5B", "5C", "6A", "6A+", "6B", "6B+", "6C", "6C+", "7A", "7A+", "7B", "7B+", "7C", "7C+", "8A", "8A+", "8B", "8B+", "8C", "8C+") YDS -> listOf("5.0", "5.1", "5.2", "5.3", "5.4", "5.5", "5.6", "5.7", "5.8", "5.9", "5.10a", "5.10b", "5.10c", "5.10d", "5.11a", "5.11b", "5.11c", "5.11d", "5.12a", "5.12b", "5.12c", "5.12d", "5.13a", "5.13b", "5.13c", "5.13d", "5.14a", "5.14b", "5.14c", "5.14d", "5.15a", "5.15b", "5.15c", "5.15d") - CUSTOM -> emptyList() // Custom allows free text input + CUSTOM -> emptyList() } companion object { /** - * Get all difficulty systems available for a specific climb type + * Get all difficulty systems based on type */ fun getSystemsForClimbType(climbType: ClimbType): List = when (climbType) { ClimbType.BOULDER -> entries.filter { it.isBoulderingSystem() } diff --git a/app/src/main/java/com/atridad/openclimb/data/model/Gym.kt b/app/src/main/java/com/atridad/openclimb/data/model/Gym.kt index e99b601..de81001 100644 --- a/app/src/main/java/com/atridad/openclimb/data/model/Gym.kt +++ b/app/src/main/java/com/atridad/openclimb/data/model/Gym.kt @@ -13,10 +13,10 @@ data class Gym( val name: String, val location: String? = null, val supportedClimbTypes: List, - val difficultySystems: List, // What systems this gym uses - val customDifficultyGrades: List = emptyList(), // For gyms using colors/custom names + val difficultySystems: List, + val customDifficultyGrades: List = emptyList(), val notes: String? = null, - val createdAt: String, // ISO string format for serialization + val createdAt: String, val updatedAt: String ) { companion object { diff --git a/app/src/main/java/com/atridad/openclimb/data/model/Problem.kt b/app/src/main/java/com/atridad/openclimb/data/model/Problem.kt index 1d465dc..80463e9 100644 --- a/app/src/main/java/com/atridad/openclimb/data/model/Problem.kt +++ b/app/src/main/java/com/atridad/openclimb/data/model/Problem.kt @@ -28,12 +28,12 @@ data class Problem( val description: String? = null, val climbType: ClimbType, val difficulty: DifficultyGrade, - val setter: String? = null, // Route setter name - val tags: List = emptyList(), // e.g., "overhang", "slab", "crimpy" - val location: String? = null, // Wall section, area in gym - val imagePaths: List = emptyList(), // Local file paths to photos - val isActive: Boolean = true, // Whether the problem is still up - val dateSet: String? = null, // When the problem was set + val setter: String? = null, + val tags: List = emptyList(), + val location: String? = null, + val imagePaths: List = emptyList(), + val isActive: Boolean = true, + val dateSet: String? = null, val notes: String? = null, val createdAt: String, val updatedAt: String diff --git a/app/src/main/java/com/atridad/openclimb/data/model/Progress.kt b/app/src/main/java/com/atridad/openclimb/data/model/Progress.kt deleted file mode 100644 index ae0e2c5..0000000 --- a/app/src/main/java/com/atridad/openclimb/data/model/Progress.kt +++ /dev/null @@ -1,50 +0,0 @@ -package com.atridad.openclimb.data.model - -import kotlinx.serialization.Serializable -import java.time.LocalDateTime - -@Serializable -data class ProblemProgress( - val problemId: String, - val totalAttempts: Int, - val successfulAttempts: Int, - val firstAttemptDate: String, - val lastAttemptDate: String, - val bestResult: AttemptResult, - val averageAttempts: Double, - val successRate: Double, - val personalBest: String? = null, // Highest hold or completion details - val notes: String? = null -) - -@Serializable -data class SessionSummary( - val sessionId: String, - val date: String, - val totalAttempts: Int, - val successfulAttempts: Int, - val uniqueProblems: Int, - val avgDifficulty: Double, - val maxDifficulty: DifficultyGrade, - val climbTypes: List, - val duration: Long?, // in minutes - val notes: String? = null -) - -@Serializable -data class ClimbingStats( - val totalSessions: Int, - val totalAttempts: Int, - val totalSuccesses: Int, - val overallSuccessRate: Double, - val uniqueProblemsAttempted: Int, - val uniqueProblemsCompleted: Int, - val averageSessionDuration: Double, // in minutes - val favoriteGym: String?, - val mostAttemptedDifficulty: DifficultyGrade?, - val currentStreak: Int, // consecutive sessions - val longestStreak: Int, - val firstClimbDate: String?, - val lastClimbDate: String?, - val improvementTrend: String? = null // "improving", "stable", "declining" -) diff --git a/app/src/main/java/com/atridad/openclimb/data/repository/ClimbRepository.kt b/app/src/main/java/com/atridad/openclimb/data/repository/ClimbRepository.kt index b399086..e24b73c 100644 --- a/app/src/main/java/com/atridad/openclimb/data/repository/ClimbRepository.kt +++ b/app/src/main/java/com/atridad/openclimb/data/repository/ClimbRepository.kt @@ -4,7 +4,6 @@ import android.content.Context import android.os.Environment import com.atridad.openclimb.data.database.OpenClimbDatabase import com.atridad.openclimb.data.model.* -import com.atridad.openclimb.utils.ImageUtils import com.atridad.openclimb.utils.ZipExportImportUtils import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first @@ -14,7 +13,7 @@ import java.io.File import java.time.LocalDateTime class ClimbRepository( - private val database: OpenClimbDatabase, + database: OpenClimbDatabase, private val context: Context ) { private val gymDao = database.gymDao() @@ -40,7 +39,6 @@ class ClimbRepository( fun getAllProblems(): Flow> = problemDao.getAllProblems() suspend fun getProblemById(id: String): Problem? = problemDao.getProblemById(id) fun getProblemsByGym(gymId: String): Flow> = problemDao.getProblemsByGym(gymId) - fun getActiveProblems(): Flow> = problemDao.getActiveProblems() suspend fun insertProblem(problem: Problem) = problemDao.insertProblem(problem) suspend fun updateProblem(problem: Problem) = problemDao.updateProblem(problem) suspend fun deleteProblem(problem: Problem) = problemDao.deleteProblem(problem) @@ -50,17 +48,14 @@ class ClimbRepository( fun getAllSessions(): Flow> = sessionDao.getAllSessions() suspend fun getSessionById(id: String): ClimbSession? = sessionDao.getSessionById(id) fun getSessionsByGym(gymId: String): Flow> = sessionDao.getSessionsByGym(gymId) - fun getRecentSessions(limit: Int = 10): Flow> = sessionDao.getRecentSessions(limit) suspend fun getActiveSession(): ClimbSession? = sessionDao.getActiveSession() fun getActiveSessionFlow(): Flow = sessionDao.getActiveSessionFlow() - fun getSessionsByStatus(status: SessionStatus): Flow> = sessionDao.getSessionsByStatus(status) suspend fun insertSession(session: ClimbSession) = sessionDao.insertSession(session) suspend fun updateSession(session: ClimbSession) = sessionDao.updateSession(session) suspend fun deleteSession(session: ClimbSession) = sessionDao.deleteSession(session) // Attempt operations fun getAllAttempts(): Flow> = attemptDao.getAllAttempts() - suspend fun getAttemptById(id: String): Attempt? = attemptDao.getAttemptById(id) fun getAttemptsBySession(sessionId: String): Flow> = attemptDao.getAttemptsBySession(sessionId) fun getAttemptsByProblem(problemId: String): Flow> = attemptDao.getAttemptsByProblem(problemId) suspend fun insertAttempt(attempt: Attempt) = attemptDao.insertAttempt(attempt) @@ -69,7 +64,7 @@ class ClimbRepository( - // JSON Export functionality + // JSON Export suspend fun exportAllDataToJson(directory: File? = null): File { val exportDir = directory ?: File(context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS), "OpenClimb") if (!exportDir.exists()) { @@ -124,12 +119,12 @@ class ClimbRepository( val jsonContent = file.readText() val importData = json.decodeFromString(jsonContent) - // Import gyms (replace if exists due to primary key constraint) + // Import gyms importData.gyms.forEach { gym -> try { gymDao.insertGym(gym) } catch (e: Exception) { - // If insertion fails due to primary key conflict, update instead + // If insertion fails, update instead gymDao.updateGym(gym) } } @@ -166,7 +161,7 @@ class ClimbRepository( } } - // ZIP Export functionality with images + // ZIP Export with images suspend fun exportAllDataToZip(directory: File? = null): File { val allGyms = gymDao.getAllGyms().first() val allProblems = problemDao.getAllProblems().first() @@ -206,7 +201,7 @@ class ClimbRepository( attempts = attempts ) - // Collect all referenced image paths + // Collect all image paths val referencedImagePaths = problems.flatMap { it.imagePaths }.toSet() ZipExportImportUtils.createExportZipToUri( @@ -228,12 +223,12 @@ class ClimbRepository( importResult.importedImagePaths ) - // Import gyms (replace if exists due to primary key constraint) + // Import gyms importData.gyms.forEach { gym -> try { gymDao.insertGym(gym) } catch (e: Exception) { - // If insertion fails due to primary key conflict, update instead + // If insertion fails update instead gymDao.updateGym(gym) } } diff --git a/app/src/main/java/com/atridad/openclimb/service/SessionTrackingService.kt b/app/src/main/java/com/atridad/openclimb/service/SessionTrackingService.kt index 6db8fa2..67dc080 100644 --- a/app/src/main/java/com/atridad/openclimb/service/SessionTrackingService.kt +++ b/app/src/main/java/com/atridad/openclimb/service/SessionTrackingService.kt @@ -6,7 +6,6 @@ import android.app.PendingIntent import android.app.Service import android.content.Context import android.content.Intent -import android.os.Build import android.os.IBinder import androidx.core.app.NotificationCompat import com.atridad.openclimb.MainActivity @@ -16,7 +15,6 @@ import com.atridad.openclimb.data.repository.ClimbRepository import kotlinx.coroutines.* import kotlinx.coroutines.flow.firstOrNull import java.time.LocalDateTime -import java.time.format.DateTimeFormatter import java.time.temporal.ChronoUnit class SessionTrackingService : Service() { @@ -113,31 +111,32 @@ class SessionTrackingService : Service() { remainingMinutes > 0 -> "${remainingMinutes}m" else -> "< 1m" } - } catch (e: Exception) { + } catch (_: Exception) { "Active" } } ?: "Active" val notification = NotificationCompat.Builder(this, CHANNEL_ID) - .setContentTitle("OpenClimb Session Active") + .setContentTitle("Climbing Session Active") .setContentText("${gym?.name ?: "Gym"} • $duration • ${attempts.size} attempts") - .setSmallIcon(R.drawable.ic_launcher_foreground) + .setSmallIcon(R.drawable.ic_mountains) .setOngoing(true) + .setPriority(NotificationCompat.PRIORITY_LOW) .setContentIntent(createOpenAppIntent()) .addAction( - R.drawable.ic_launcher_foreground, + R.drawable.ic_mountains, "Open Session", createOpenAppIntent() ) .addAction( - R.drawable.ic_launcher_foreground, + android.R.drawable.ic_menu_close_clear_cancel, "End Session", createStopIntent() ) .build() startForeground(NOTIFICATION_ID, notification) - } catch (e: Exception) { + } catch (_: Exception) { // Handle errors gracefully stopSessionTracking() } @@ -166,19 +165,17 @@ class SessionTrackingService : Service() { } private fun createNotificationChannel() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val channel = NotificationChannel( - CHANNEL_ID, - "Session Tracking", - NotificationManager.IMPORTANCE_LOW - ).apply { - description = "Shows active climbing session information" - setShowBadge(false) - } - - val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - notificationManager.createNotificationChannel(channel) + val channel = NotificationChannel( + CHANNEL_ID, + "Session Tracking", + NotificationManager.IMPORTANCE_LOW + ).apply { + description = "Shows active climbing session information" + setShowBadge(false) } + + val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager + notificationManager.createNotificationChannel(channel) } override fun onDestroy() { diff --git a/app/src/main/java/com/atridad/openclimb/ui/OpenClimbApp.kt b/app/src/main/java/com/atridad/openclimb/ui/OpenClimbApp.kt index 357d4b6..fccd9b2 100644 --- a/app/src/main/java/com/atridad/openclimb/ui/OpenClimbApp.kt +++ b/app/src/main/java/com/atridad/openclimb/ui/OpenClimbApp.kt @@ -86,9 +86,6 @@ fun OpenClimbApp() { viewModel = viewModel, onNavigateToSessionDetail = { sessionId -> navController.navigate(Screen.SessionDetail(sessionId)) - }, - onNavigateToAddSession = { gymId -> - navController.navigate(Screen.AddEditSession(gymId = gymId)) } ) } @@ -112,9 +109,6 @@ fun OpenClimbApp() { viewModel = viewModel, onNavigateToProblemDetail = { problemId -> navController.navigate(Screen.ProblemDetail(problemId)) - }, - onNavigateToAddProblem = { gymId -> - navController.navigate(Screen.AddEditProblem(gymId = gymId)) } ) } @@ -140,9 +134,6 @@ fun OpenClimbApp() { viewModel = viewModel, onNavigateToGymDetail = { gymId -> navController.navigate(Screen.GymDetail(gymId)) - }, - onNavigateToAddGym = { - navController.navigate(Screen.AddEditGym()) } ) } diff --git a/app/src/main/java/com/atridad/openclimb/ui/components/ActiveSessionBanner.kt b/app/src/main/java/com/atridad/openclimb/ui/components/ActiveSessionBanner.kt index 7f2fb99..47f0bed 100644 --- a/app/src/main/java/com/atridad/openclimb/ui/components/ActiveSessionBanner.kt +++ b/app/src/main/java/com/atridad/openclimb/ui/components/ActiveSessionBanner.kt @@ -5,12 +5,10 @@ import androidx.compose.foundation.layout.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.PlayArrow -import androidx.compose.material.icons.rounded.Close import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import com.atridad.openclimb.data.model.ClimbSession @@ -95,80 +93,6 @@ fun ActiveSessionBanner( } } -@Composable -fun StartSessionButton( - gyms: List, - onStartSession: (String) -> Unit -) { - var showGymSelection by remember { mutableStateOf(false) } - - if (gyms.isEmpty()) { - Card( - modifier = Modifier.fillMaxWidth(), - colors = CardDefaults.cardColors( - containerColor = MaterialTheme.colorScheme.surfaceVariant - ) - ) { - Column( - modifier = Modifier - .fillMaxWidth() - .padding(16.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text( - text = "No gyms available", - style = MaterialTheme.typography.titleMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) - Text( - text = "Add a gym first to start a session", - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) - } - } - } else { - Button( - onClick = { showGymSelection = true }, - modifier = Modifier.fillMaxWidth() - ) { - Icon(Icons.Default.PlayArrow, contentDescription = null) - Spacer(modifier = Modifier.width(8.dp)) - Text("Start Session") - } - } - - if (showGymSelection) { - AlertDialog( - onDismissRequest = { showGymSelection = false }, - title = { Text("Select Gym") }, - text = { - Column { - gyms.forEach { gym -> - TextButton( - onClick = { - onStartSession(gym.id) - showGymSelection = false - }, - modifier = Modifier.fillMaxWidth() - ) { - Text( - text = gym.name, - modifier = Modifier.fillMaxWidth() - ) - } - } - } - }, - confirmButton = { - TextButton(onClick = { showGymSelection = false }) { - Text("Cancel") - } - } - ) - } -} - private fun calculateDuration(startTimeString: String): String { return try { val startTime = LocalDateTime.parse(startTimeString) @@ -182,7 +106,7 @@ private fun calculateDuration(startTimeString: String): String { remainingMinutes > 0 -> "${remainingMinutes}m" else -> "< 1m" } - } catch (e: Exception) { + } catch (_: Exception) { "Active" } } diff --git a/app/src/main/java/com/atridad/openclimb/ui/components/FullscreenImageViewer.kt b/app/src/main/java/com/atridad/openclimb/ui/components/FullscreenImageViewer.kt index c832536..e4d74ed 100644 --- a/app/src/main/java/com/atridad/openclimb/ui/components/FullscreenImageViewer.kt +++ b/app/src/main/java/com/atridad/openclimb/ui/components/FullscreenImageViewer.kt @@ -50,7 +50,7 @@ fun FullscreenImageViewer( LaunchedEffect(pagerState.currentPage) { thumbnailListState.animateScrollToItem( index = pagerState.currentPage, - scrollOffset = -200 // Center the item + scrollOffset = -200 ) } diff --git a/app/src/main/java/com/atridad/openclimb/ui/components/ImageDisplay.kt b/app/src/main/java/com/atridad/openclimb/ui/components/ImageDisplay.kt index 246b913..61cbb90 100644 --- a/app/src/main/java/com/atridad/openclimb/ui/components/ImageDisplay.kt +++ b/app/src/main/java/com/atridad/openclimb/ui/components/ImageDisplay.kt @@ -7,7 +7,6 @@ import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.* import androidx.compose.runtime.* -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.layout.ContentScale diff --git a/app/src/main/java/com/atridad/openclimb/ui/components/ImagePicker.kt b/app/src/main/java/com/atridad/openclimb/ui/components/ImagePicker.kt index 61b7152..78e34e3 100644 --- a/app/src/main/java/com/atridad/openclimb/ui/components/ImagePicker.kt +++ b/app/src/main/java/com/atridad/openclimb/ui/components/ImagePicker.kt @@ -1,6 +1,5 @@ package com.atridad.openclimb.ui.components -import android.net.Uri import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.layout.* @@ -20,7 +19,6 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import coil.compose.AsyncImage import com.atridad.openclimb.utils.ImageUtils -import java.io.File @Composable fun ImagePicker( @@ -41,7 +39,7 @@ fun ImagePicker( val remainingSlots = maxImages - currentCount val urisToProcess = uris.take(remainingSlots) - // Process each selected image + // Process images val newImagePaths = mutableListOf() urisToProcess.forEach { uri -> val imagePath = ImageUtils.saveImageFromUri(context, uri) diff --git a/app/src/main/java/com/atridad/openclimb/ui/screens/AddEditScreens.kt b/app/src/main/java/com/atridad/openclimb/ui/screens/AddEditScreens.kt index 7cf7cb0..37184f6 100644 --- a/app/src/main/java/com/atridad/openclimb/ui/screens/AddEditScreens.kt +++ b/app/src/main/java/com/atridad/openclimb/ui/screens/AddEditScreens.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.selection.selectable import androidx.compose.foundation.selection.selectableGroup import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.* import androidx.compose.material3.* import androidx.compose.runtime.* @@ -81,7 +82,7 @@ fun AddEditGymScreen( title = { Text(if (isEditing) "Edit Gym" else "Add Gym") }, navigationIcon = { IconButton(onClick = onNavigateBack) { - Icon(Icons.Default.ArrowBack, contentDescription = "Back") + Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back") } }, actions = { @@ -90,7 +91,7 @@ fun AddEditGymScreen( val gym = Gym.create(name, location, selectedClimbTypes.toList(), selectedDifficultySystems.toList(), notes = notes) if (isEditing) { - viewModel.updateGym(gym.copy(id = gymId!!)) + viewModel.updateGym(gym.copy(id = gymId)) } else { viewModel.addGym(gym) } @@ -291,7 +292,7 @@ fun AddEditProblemScreen( val availableClimbTypes = selectedGym?.supportedClimbTypes ?: ClimbType.entries.toList() val availableDifficultySystems = DifficultySystem.getSystemsForClimbType(selectedClimbType).filter { system -> - selectedGym?.difficultySystems?.contains(system) ?: true + selectedGym?.difficultySystems?.contains(system) != false } // Auto-select climb type if there's only one available @@ -329,7 +330,7 @@ fun AddEditProblemScreen( title = { Text(if (isEditing) "Edit Problem" else "Add Problem") }, navigationIcon = { IconButton(onClick = onNavigateBack) { - Icon(Icons.Default.ArrowBack, contentDescription = "Back") + Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back") } }, actions = { @@ -359,7 +360,7 @@ fun AddEditProblemScreen( ) if (isEditing) { - viewModel.updateProblem(problem.copy(id = problemId!!)) + viewModel.updateProblem(problem.copy(id = problemId)) } else { viewModel.addProblem(problem) } @@ -646,7 +647,7 @@ fun AddEditProblemScreen( label = { Text("Tags (Optional)") }, modifier = Modifier.fillMaxWidth(), singleLine = true, - placeholder = { Text("e.g., overhang, crimpy, dynamic (comma-separated)") } + placeholder = { Text("e.g., crimpy, dynamic (comma-separated)") } ) Spacer(modifier = Modifier.height(8.dp)) @@ -736,7 +737,7 @@ fun AddEditSessionScreen( title = { Text(if (isEditing) "Edit Session" else "Add Session") }, navigationIcon = { IconButton(onClick = onNavigateBack) { - Icon(Icons.Default.ArrowBack, contentDescription = "Back") + Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back") } }, actions = { @@ -749,7 +750,7 @@ fun AddEditSessionScreen( ) if (isEditing) { - viewModel.updateSession(session.copy(id = sessionId!!)) + viewModel.updateSession(session.copy(id = sessionId)) } else { viewModel.addSession(session) diff --git a/app/src/main/java/com/atridad/openclimb/ui/screens/DetailScreens.kt b/app/src/main/java/com/atridad/openclimb/ui/screens/DetailScreens.kt index 8dea6fa..eaaf541 100644 --- a/app/src/main/java/com/atridad/openclimb/ui/screens/DetailScreens.kt +++ b/app/src/main/java/com/atridad/openclimb/ui/screens/DetailScreens.kt @@ -8,10 +8,9 @@ import androidx.compose.foundation.lazy.items import androidx.compose.foundation.selection.selectable import androidx.compose.foundation.selection.selectableGroup import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.Add -import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Edit @@ -87,7 +86,7 @@ fun SessionDetailScreen( title = { Text("Session Details") }, navigationIcon = { IconButton(onClick = onNavigateBack) { - Icon(Icons.Default.ArrowBack, contentDescription = "Back") + Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back") } }, actions = { @@ -403,7 +402,7 @@ fun ProblemDetailScreen( val context = LocalContext.current var showDeleteDialog by remember { mutableStateOf(false) } var showImageViewer by remember { mutableStateOf(false) } - var selectedImageIndex by remember { mutableStateOf(0) } + var selectedImageIndex by remember { mutableIntStateOf(0) } val attempts by viewModel.getAttemptsByProblem(problemId).collectAsState(initial = emptyList()) val sessions by viewModel.sessions.collectAsState() val gyms by viewModel.gyms.collectAsState() @@ -436,7 +435,7 @@ fun ProblemDetailScreen( title = { Text("Problem Details") }, navigationIcon = { IconButton(onClick = onNavigateBack) { - Icon(Icons.Default.ArrowBack, contentDescription = "Back") + Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back") } }, actions = { @@ -770,7 +769,7 @@ fun GymDetailScreen( title = { Text(gym?.name ?: "Gym Details") }, navigationIcon = { IconButton(onClick = onNavigateBack) { - Icon(Icons.Default.ArrowBack, contentDescription = "Back") + Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back") } }, actions = { @@ -1087,7 +1086,7 @@ fun GymDetailScreen( supportingContent = { val dateTime = try { LocalDateTime.parse(session.date) - } catch (e: Exception) { + } catch (_: Exception) { null } val formattedDate = dateTime?.format( @@ -1348,42 +1347,11 @@ private fun formatDate(dateString: String): String { val date = LocalDateTime.parse(dateString, formatter) val displayFormatter = DateTimeFormatter.ofPattern("MMM dd, yyyy") date.format(displayFormatter) - } catch (e: Exception) { + } catch (_: Exception) { dateString.take(10) // Fallback to just the date part } } -private fun formatTime(timeString: String): String { - return try { - val formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME - val time = LocalDateTime.parse(timeString, formatter) - val displayFormatter = DateTimeFormatter.ofPattern("h:mm a") - time.format(displayFormatter) - } catch (e: Exception) { - timeString.take(8) // Fallback to time part - } -} - -private fun calculateSessionDuration(startTime: String, endTime: String): String { - return try { - val formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME - val start = LocalDateTime.parse(startTime, formatter) - val end = LocalDateTime.parse(endTime, formatter) - val duration = java.time.Duration.between(start, end) - - val hours = duration.toHours() - val minutes = duration.toMinutes() % 60 - - when { - hours > 0 -> "${hours}h ${minutes}m" - minutes > 0 -> "${minutes}m" - else -> "< 1m" - } - } catch (e: Exception) { - "Unknown" - } -} - @OptIn(ExperimentalMaterial3Api::class) @Composable fun EnhancedAddAttemptDialog( @@ -1896,28 +1864,5 @@ fun EnhancedAddAttemptDialog( } } } - -@Composable -fun StatisticItem( - label: String, - value: String, - valueColor: androidx.compose.ui.graphics.Color = MaterialTheme.colorScheme.primary -) { - Column( - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text( - text = value, - style = MaterialTheme.typography.headlineSmall, - fontWeight = FontWeight.Bold, - color = valueColor - ) - Text( - text = label, - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) } } -} -} diff --git a/app/src/main/java/com/atridad/openclimb/ui/screens/GymsScreen.kt b/app/src/main/java/com/atridad/openclimb/ui/screens/GymsScreen.kt index 137c2da..18c36c2 100644 --- a/app/src/main/java/com/atridad/openclimb/ui/screens/GymsScreen.kt +++ b/app/src/main/java/com/atridad/openclimb/ui/screens/GymsScreen.kt @@ -3,15 +3,12 @@ package com.atridad.openclimb.ui.screens import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Add import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import com.atridad.openclimb.R import com.atridad.openclimb.data.model.Gym @@ -21,8 +18,7 @@ import com.atridad.openclimb.ui.viewmodel.ClimbViewModel @Composable fun GymsScreen( viewModel: ClimbViewModel, - onNavigateToGymDetail: (String) -> Unit, - onNavigateToAddGym: () -> Unit + onNavigateToGymDetail: (String) -> Unit ) { val gyms by viewModel.gyms.collectAsState() diff --git a/app/src/main/java/com/atridad/openclimb/ui/screens/ProblemsScreen.kt b/app/src/main/java/com/atridad/openclimb/ui/screens/ProblemsScreen.kt index 7b7d858..80084e1 100644 --- a/app/src/main/java/com/atridad/openclimb/ui/screens/ProblemsScreen.kt +++ b/app/src/main/java/com/atridad/openclimb/ui/screens/ProblemsScreen.kt @@ -4,15 +4,12 @@ import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Add import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import com.atridad.openclimb.R import com.atridad.openclimb.data.model.ClimbType @@ -26,14 +23,13 @@ import com.atridad.openclimb.ui.viewmodel.ClimbViewModel @Composable fun ProblemsScreen( viewModel: ClimbViewModel, - onNavigateToProblemDetail: (String) -> Unit, - onNavigateToAddProblem: (String?) -> Unit + onNavigateToProblemDetail: (String) -> Unit ) { val problems by viewModel.problems.collectAsState() val gyms by viewModel.gyms.collectAsState() var showImageViewer by remember { mutableStateOf(false) } var selectedImagePaths by remember { mutableStateOf>(emptyList()) } - var selectedImageIndex by remember { mutableStateOf(0) } + var selectedImageIndex by remember { mutableIntStateOf(0) } // Filter state var selectedClimbType by remember { mutableStateOf(null) } @@ -41,8 +37,8 @@ fun ProblemsScreen( // Apply filters val filteredProblems = problems.filter { problem -> - val climbTypeMatch = selectedClimbType?.let { it == problem.climbType } ?: true - val gymMatch = selectedGym?.let { it.id == problem.gymId } ?: true + val climbTypeMatch = selectedClimbType?.let { it == problem.climbType } != false + val gymMatch = selectedGym?.let { it.id == problem.gymId } != false climbTypeMatch && gymMatch } diff --git a/app/src/main/java/com/atridad/openclimb/ui/screens/SessionsScreen.kt b/app/src/main/java/com/atridad/openclimb/ui/screens/SessionsScreen.kt index 744e716..22a439e 100644 --- a/app/src/main/java/com/atridad/openclimb/ui/screens/SessionsScreen.kt +++ b/app/src/main/java/com/atridad/openclimb/ui/screens/SessionsScreen.kt @@ -3,8 +3,10 @@ package com.atridad.openclimb.ui.screens import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.CheckCircle +import androidx.compose.material.icons.filled.Warning import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment @@ -26,13 +28,13 @@ import java.time.format.DateTimeFormatter @Composable fun SessionsScreen( viewModel: ClimbViewModel, - onNavigateToSessionDetail: (String) -> Unit, - onNavigateToAddSession: (String?) -> Unit + onNavigateToSessionDetail: (String) -> Unit ) { val context = LocalContext.current val sessions by viewModel.sessions.collectAsState() val gyms by viewModel.gyms.collectAsState() val activeSession by viewModel.activeSession.collectAsState() + val uiState by viewModel.uiState.collectAsState() // Filter out active sessions from regular session list val completedSessions = sessions.filter { it.status == SessionStatus.COMPLETED } @@ -103,6 +105,79 @@ fun SessionsScreen( } } } + + // Show UI state messages and errors + uiState.message?.let { message -> + LaunchedEffect(message) { + kotlinx.coroutines.delay(5000) + viewModel.clearMessage() + } + + Card( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.primaryContainer + ), + shape = RoundedCornerShape(12.dp) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + Icons.Default.CheckCircle, + contentDescription = null, + tint = MaterialTheme.colorScheme.primary + ) + Spacer(modifier = Modifier.width(8.dp)) + Text( + text = message, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onPrimaryContainer + ) + } + } + } + + uiState.error?.let { error -> + LaunchedEffect(error) { + kotlinx.coroutines.delay(5000) + viewModel.clearError() + } + + Card( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.errorContainer + ), + shape = RoundedCornerShape(12.dp) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + Icons.Default.Warning, + contentDescription = null, + tint = MaterialTheme.colorScheme.error + ) + Spacer(modifier = Modifier.width(8.dp)) + Text( + text = error, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onErrorContainer + ) + } + } + } } @OptIn(ExperimentalMaterial3Api::class) @@ -141,7 +216,7 @@ fun SessionCard( session.duration?.let { duration -> Text( - text = "Duration: ${duration} minutes", + text = "Duration: $duration minutes", style = MaterialTheme.typography.bodyMedium ) } @@ -203,7 +278,7 @@ private fun formatDate(dateString: String): String { return try { val date = LocalDateTime.parse(dateString.split("T")[0] + "T00:00:00") date.format(DateTimeFormatter.ofPattern("MMM dd, yyyy")) - } catch (e: Exception) { + } catch (_: Exception) { dateString } } diff --git a/app/src/main/java/com/atridad/openclimb/ui/screens/SettingsScreen.kt b/app/src/main/java/com/atridad/openclimb/ui/screens/SettingsScreen.kt index 82f01a4..f25cff3 100644 --- a/app/src/main/java/com/atridad/openclimb/ui/screens/SettingsScreen.kt +++ b/app/src/main/java/com/atridad/openclimb/ui/screens/SettingsScreen.kt @@ -2,7 +2,6 @@ package com.atridad.openclimb.ui.screens import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts -import android.os.Environment import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.shape.RoundedCornerShape diff --git a/app/src/main/java/com/atridad/openclimb/ui/theme/Color.kt b/app/src/main/java/com/atridad/openclimb/ui/theme/Color.kt index aaab909..989ace4 100644 --- a/app/src/main/java/com/atridad/openclimb/ui/theme/Color.kt +++ b/app/src/main/java/com/atridad/openclimb/ui/theme/Color.kt @@ -63,13 +63,4 @@ val ClimbNeutralVariant30 = Color(0xFF484848) val ClimbNeutralVariant50 = Color(0xFF797979) val ClimbNeutralVariant60 = Color(0xFF939393) val ClimbNeutralVariant80 = Color(0xFFC7C7C7) -val ClimbNeutralVariant90 = Color(0xFFE3E3E3) - -// Legacy colors for backward compatibility -val Purple80 = ClimbOrange80 -val PurpleGrey80 = ClimbGrey80 -val Pink80 = ClimbBlue80 - -val Purple40 = ClimbOrange40 -val PurpleGrey40 = ClimbGrey40 -val Pink40 = ClimbBlue40 \ No newline at end of file +val ClimbNeutralVariant90 = Color(0xFFE3E3E3) \ No newline at end of file diff --git a/app/src/main/java/com/atridad/openclimb/ui/theme/Theme.kt b/app/src/main/java/com/atridad/openclimb/ui/theme/Theme.kt index 705e6f0..1a8ecf8 100644 --- a/app/src/main/java/com/atridad/openclimb/ui/theme/Theme.kt +++ b/app/src/main/java/com/atridad/openclimb/ui/theme/Theme.kt @@ -1,7 +1,6 @@ package com.atridad.openclimb.ui.theme import android.app.Activity -import android.os.Build import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.MaterialTheme import androidx.compose.material3.darkColorScheme @@ -10,7 +9,6 @@ import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable import androidx.compose.runtime.SideEffect -import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalView import androidx.core.view.WindowCompat @@ -98,7 +96,7 @@ fun OpenClimbTheme( content: @Composable () -> Unit ) { val colorScheme = when { - dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + dynamicColor && true -> { val context = LocalContext.current if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) } diff --git a/app/src/main/java/com/atridad/openclimb/utils/ImageUtils.kt b/app/src/main/java/com/atridad/openclimb/utils/ImageUtils.kt index 448cf57..4ce87a2 100644 --- a/app/src/main/java/com/atridad/openclimb/utils/ImageUtils.kt +++ b/app/src/main/java/com/atridad/openclimb/utils/ImageUtils.kt @@ -1,13 +1,14 @@ package com.atridad.openclimb.utils +import android.annotation.SuppressLint import android.content.Context import android.graphics.Bitmap import android.graphics.BitmapFactory import android.net.Uri import java.io.File import java.io.FileOutputStream -import java.io.IOException import java.util.UUID +import androidx.core.graphics.scale object ImageUtils { @@ -65,6 +66,7 @@ object ImageUtils { /** * Compresses and resizes an image bitmap */ + @SuppressLint("UseKtx") private fun compressImage(original: Bitmap): Bitmap { val width = original.width val height = original.height @@ -79,7 +81,7 @@ object ImageUtils { return if (scaleFactor < 1f) { val newWidth = (width * scaleFactor).toInt() val newHeight = (height * scaleFactor).toInt() - Bitmap.createScaledBitmap(original, newWidth, newHeight, true) + original.scale(newWidth, newHeight) } else { original } @@ -110,30 +112,7 @@ object ImageUtils { false } } - - /** - * Copies an image file to export directory - * @param context Android context - * @param relativePath The relative path of the image - * @param exportDir The directory to copy to - * @return The filename in the export directory, null if failed - */ - fun copyImageForExport(context: Context, relativePath: String, exportDir: File): String? { - return try { - val sourceFile = getImageFile(context, relativePath) - if (!sourceFile.exists()) return null - - val filename = sourceFile.name - val destFile = File(exportDir, filename) - - sourceFile.copyTo(destFile, overwrite = true) - filename - } catch (e: Exception) { - e.printStackTrace() - null - } - } - + /** * Imports an image file from the import directory * @param context Android context 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 4ec52b0..6f2b3dc 100644 --- a/app/src/main/java/com/atridad/openclimb/utils/SessionShareUtils.kt +++ b/app/src/main/java/com/atridad/openclimb/utils/SessionShareUtils.kt @@ -11,6 +11,8 @@ import java.io.FileOutputStream import java.time.LocalDateTime import java.time.format.DateTimeFormatter import kotlin.math.roundToInt +import androidx.core.graphics.createBitmap +import androidx.core.graphics.toColorInt object SessionShareUtils { @@ -75,31 +77,7 @@ object SessionShareUtils { topResult = topResult ) } - - private fun calculateDuration(startTime: String?, endTime: String?): String { - return try { - if (startTime != null && endTime != null) { - val formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME - val start = LocalDateTime.parse(startTime, formatter) - val end = LocalDateTime.parse(endTime, formatter) - val duration = java.time.Duration.between(start, end) - - val hours = duration.toHours() - val minutes = duration.toMinutes() % 60 - - when { - hours > 0 -> "${hours}h ${minutes}m" - minutes > 0 -> "${minutes}m" - else -> "< 1m" - } - } else { - "Unknown" - } - } catch (e: Exception) { - "Unknown" - } - } - + fun generateShareCard( context: Context, session: ClimbSession, @@ -110,14 +88,14 @@ object SessionShareUtils { val width = 1080 val height = 1350 - val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) + val bitmap = createBitmap(width, height) val canvas = Canvas(bitmap) val gradientDrawable = GradientDrawable( GradientDrawable.Orientation.TOP_BOTTOM, intArrayOf( - Color.parseColor("#667eea"), - Color.parseColor("#764ba2") + "#667eea".toColorInt(), + "#764ba2".toColorInt() ) ) gradientDrawable.setBounds(0, 0, width, height) @@ -133,7 +111,7 @@ object SessionShareUtils { } val subtitlePaint = Paint().apply { - color = Color.parseColor("#E8E8E8") + color = "#E8E8E8".toColorInt() textSize = 48f typeface = Typeface.DEFAULT isAntiAlias = true @@ -141,7 +119,7 @@ object SessionShareUtils { } val statLabelPaint = Paint().apply { - color = Color.parseColor("#B8B8B8") + color = "#B8B8B8".toColorInt() textSize = 36f typeface = Typeface.DEFAULT isAntiAlias = true @@ -157,7 +135,7 @@ object SessionShareUtils { } val cardPaint = Paint().apply { - color = Color.parseColor("#40FFFFFF") + color = "#40FFFFFF".toColorInt() isAntiAlias = true } @@ -211,7 +189,7 @@ object SessionShareUtils { // App branding val brandingPaint = Paint().apply { - color = Color.parseColor("#80FFFFFF") + color = "#80FFFFFF".toColorInt() textSize = 32f typeface = Typeface.DEFAULT isAntiAlias = true @@ -268,7 +246,7 @@ object SessionShareUtils { // Background arc val bgPaint = Paint().apply { - color = Color.parseColor("#40FFFFFF") + color = "#40FFFFFF".toColorInt() style = Paint.Style.STROKE this.strokeWidth = strokeWidth isAntiAlias = true @@ -277,7 +255,7 @@ object SessionShareUtils { // Success arc val successPaint = Paint().apply { - color = Color.parseColor("#4CAF50") + color = "#4CAF50".toColorInt() style = Paint.Style.STROKE this.strokeWidth = strokeWidth isAntiAlias = true @@ -305,7 +283,7 @@ object SessionShareUtils { val date = LocalDateTime.parse(dateString, formatter) val displayFormatter = DateTimeFormatter.ofPattern("MMMM dd, yyyy") date.format(displayFormatter) - } catch (e: Exception) { + } catch (_: Exception) { dateString.take(10) } } diff --git a/app/src/main/java/com/atridad/openclimb/utils/ZipExportImportUtils.kt b/app/src/main/java/com/atridad/openclimb/utils/ZipExportImportUtils.kt index 0438e44..9c1f2eb 100644 --- a/app/src/main/java/com/atridad/openclimb/utils/ZipExportImportUtils.kt +++ b/app/src/main/java/com/atridad/openclimb/utils/ZipExportImportUtils.kt @@ -1,7 +1,6 @@ package com.atridad.openclimb.utils import android.content.Context -import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import java.io.File import java.io.FileInputStream @@ -179,14 +178,7 @@ object ZipExportImportUtils { return ImportResult(jsonContent, importedImagePaths) } - - /** - * Utility function to determine if a file is a ZIP file based on extension - */ - fun isZipFile(filename: String): Boolean { - return filename.lowercase().endsWith(".zip") - } - + /** * Updates image paths in a problem list after import * This function maps the old image paths to the new ones after import