Skip to content

Tablet fixes#89

Merged
9thLevelSoftware merged 28 commits into
mainfrom
tablet_fixes
Jan 4, 2026
Merged

Tablet fixes#89
9thLevelSoftware merged 28 commits into
mainfrom
tablet_fixes

Conversation

@9thLevelSoftware

Copy link
Copy Markdown
Owner

No description provided.

9thLevelSoftware and others added 28 commits January 4, 2026 13:12
Provides LocalWindowSizeClass to all child composables for responsive layouts.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Uses 80% of screen height, capped between 400-800dp.
Closes #87

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Sheets now use 80% of screen height, capped between 300-600dp.
Closes #72

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Dialog scales from 280dp (phone) to 400dp (tablet).
Applies to both AutoStopOverlay and AutoStartOverlay components.
Closes #74

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Scales from 320dp (phone) to 480dp (expanded tablet).
Partial fix for #67

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Partial fix for #67

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Partial fix for #67

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add ResponsiveDimensions import for tablet-aware sizing
- Make Y-axis column width responsive (50dp compact, 65dp medium, 80dp expanded)
- Adjust X-axis label padding to match responsive column width

Closes #85, partial fix for #67

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Cards now have max-width on tablets to prevent over-stretching.
Closes #68

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- FAB spacer scales with screen size (180dp/210dp/240dp)
- Hero card image scales appropriately (200dp/240dp/280dp)
- ActiveCycleHero height adapts to tablets (180dp/210dp/240dp)

Closes #69

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Metric display scales with screen size using ResponsiveDimensions
- Button heights adapt to tablets via WindowSizeClass

Closes #73, partial #86

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Grid columns: 3 (phone) -> 4 (medium) -> 6 (expanded)

Closes #80

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add imports for LocalWindowSizeClass, WindowWidthSizeClass, ResponsiveDimensions
- Replace fixed .height(64.dp) with responsive buttonHeight (64/72/80dp)
- Replace fixed .size(200.dp) on countdown overlay with ResponsiveDimensions.componentSize()

Closes #86

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add LocalWindowSizeClass and WindowWidthSizeClass imports
- Make tab indicator height responsive (8dp/10dp/12dp for compact/medium/expanded)
- Make FAB icon size responsive (28dp/32dp/36dp for compact/medium/expanded)

Closes #70

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add responsive width calculation to LiveMetricsCard weight labels
for cable position displays. Labels now scale from 50dp (compact)
to 80dp (expanded) based on window size class.

Closes #84

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Closes #71

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Closes #81

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…lets

Use WindowSizeClass to set horizontal padding - 24dp for compact,
36dp for medium, and 48dp for expanded displays. Issue #77.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use BoxWithConstraints to calculate preview height dynamically based on
available space (40% of maxHeight, constrained between 150-400dp).
Issue #79.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Convert fixed .width(60.dp), .width(100.dp), and .width(120.dp) to
flexible weight-based layouts for better tablet and responsive display.

Fixes #76

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…ponents

Add responsiveDimension() helper function that scales dimensions based
on WindowWidthSizeClass:
- Expanded: 1.4x base size
- Medium: 1.2x base size
- Compact: base size (original)

Apply to WorkoutHistoryCardSkeleton, PersonalRecordCardSkeleton, and
RoutineCardSkeleton for consistent tablet scaling of skeleton loaders.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings January 4, 2026 21:22
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@9thLevelSoftware

Copy link
Copy Markdown
Owner Author

@copilot Review please

Copilot AI commented Jan 4, 2026

Copy link
Copy Markdown
Contributor

@DasBluEyedDevil I've opened a new pull request, #90, to work on those changes. Once the pull request is ready, I'll request review from you.

@9thLevelSoftware 9thLevelSoftware merged commit 0961cbb into main Jan 4, 2026
7 checks passed
@9thLevelSoftware 9thLevelSoftware deleted the tablet_fixes branch January 4, 2026 21:23

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements comprehensive tablet support improvements across the application, introduces a responsive design system based on Material 3 window size classes, fixes database migration issues, and enhances CSV export functionality with proper thread handling and error logging.

Key Changes

  • Introduces a new WindowSizeClass utility system with responsive dimension helpers for adaptive UI layouts across phones, tablets, and larger screens
  • Implements tablet-responsive sizing throughout the app for buttons, charts, cards, dialogs, and components using breakpoints at 600dp and 840dp
  • Adds resilient database migration handling to prevent crashes from partial migrations and fixes startup issues for users upgrading to the Training Cycles feature

Reviewed changes

Copilot reviewed 31 out of 32 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
WindowSizeClass.kt New responsive design system with Material 3 breakpoints and dimension helpers
CsvExporter.ios.kt Adds main thread dispatch for UIKit operations to prevent crashes
6.sqm Database migration for Training Cycles feature tables and indexes
DriverFactory.android.kt Implements resilient migration handling with duplicate column/table error recovery
WorkoutTabAlt.kt, WorkoutTab.kt, WorkoutHud.kt Responsive button heights and component sizing based on screen size
InsightsTab.kt Adds ResponsiveCardWrapper to prevent card over-stretching on tablets
HomeScreen.kt Responsive spacing and hero card dimensions for tablets
ExerciseEditDialog.kt Improved layout weights for better tablet display
EnhancedMainScreen.kt Integrates WindowSizeClass provider at root level
Various chart components Responsive chart heights using ResponsiveDimensions helpers
ShimmerEffect.kt Responsive skeleton loading card dimensions
Various other components Responsive dialog widths, panel widths, grid columns, and spacing
CsvExporter.android.kt Enhanced error logging for debugging
file_paths.xml FileProvider configuration for CSV sharing
AndroidManifest.xml Adds FileProvider declaration

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +36 to +38
// Extension for Dp multiplication
private operator fun Dp.times(factor: Float): Dp = (this.value * factor).dp

Copilot AI Jan 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Dp multiplication extension operator is duplicated. This same extension is already defined in WindowSizeClass.kt at line 134. Consider consolidating this into a shared utility file or removing one of the duplicates to follow the DRY principle.

Suggested change
// Extension for Dp multiplication
private operator fun Dp.times(factor: Float): Dp = (this.value * factor).dp

Copilot uses AI. Check for mistakes.
Comment on lines +88 to +195
private fun getMigrationStatements(version: Int): List<String> {
return when (version) {
5 -> listOf(
"ALTER TABLE WorkoutSession ADD COLUMN peakForceConcentricA REAL",
"ALTER TABLE WorkoutSession ADD COLUMN peakForceConcentricB REAL",
"ALTER TABLE WorkoutSession ADD COLUMN peakForceEccentricA REAL",
"ALTER TABLE WorkoutSession ADD COLUMN peakForceEccentricB REAL",
"ALTER TABLE WorkoutSession ADD COLUMN avgForceConcentricA REAL",
"ALTER TABLE WorkoutSession ADD COLUMN avgForceConcentricB REAL",
"ALTER TABLE WorkoutSession ADD COLUMN avgForceEccentricA REAL",
"ALTER TABLE WorkoutSession ADD COLUMN avgForceEccentricB REAL",
"ALTER TABLE WorkoutSession ADD COLUMN heaviestLiftKg REAL",
"ALTER TABLE WorkoutSession ADD COLUMN totalVolumeKg REAL",
"ALTER TABLE WorkoutSession ADD COLUMN estimatedCalories REAL",
"ALTER TABLE WorkoutSession ADD COLUMN warmupAvgWeightKg REAL",
"ALTER TABLE WorkoutSession ADD COLUMN workingAvgWeightKg REAL",
"ALTER TABLE WorkoutSession ADD COLUMN burnoutAvgWeightKg REAL",
"ALTER TABLE WorkoutSession ADD COLUMN peakWeightKg REAL",
"ALTER TABLE WorkoutSession ADD COLUMN rpe INTEGER"
)
6 -> listOf(
"""CREATE TABLE IF NOT EXISTS TrainingCycle (
id TEXT PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
description TEXT,
created_at INTEGER NOT NULL,
is_active INTEGER NOT NULL DEFAULT 0
)""",
"""CREATE TABLE IF NOT EXISTS CycleDay (
id TEXT PRIMARY KEY NOT NULL,
cycle_id TEXT NOT NULL,
day_number INTEGER NOT NULL,
name TEXT,
routine_id TEXT,
is_rest_day INTEGER NOT NULL DEFAULT 0,
echo_level TEXT,
eccentric_load_percent INTEGER,
weight_progression_percent REAL,
rep_modifier INTEGER,
rest_time_override_seconds INTEGER,
FOREIGN KEY (cycle_id) REFERENCES TrainingCycle(id) ON DELETE CASCADE,
FOREIGN KEY (routine_id) REFERENCES Routine(id) ON DELETE SET NULL
)""",
"""CREATE TABLE IF NOT EXISTS CycleProgress (
id TEXT PRIMARY KEY NOT NULL,
cycle_id TEXT NOT NULL UNIQUE,
current_day_number INTEGER NOT NULL DEFAULT 1,
last_completed_date INTEGER,
cycle_start_date INTEGER NOT NULL,
last_advanced_at INTEGER,
completed_days TEXT,
missed_days TEXT,
rotation_count INTEGER NOT NULL DEFAULT 0,
FOREIGN KEY (cycle_id) REFERENCES TrainingCycle(id) ON DELETE CASCADE
)""",
"""CREATE TABLE IF NOT EXISTS CycleProgression (
cycle_id TEXT PRIMARY KEY NOT NULL,
frequency_cycles INTEGER NOT NULL DEFAULT 2,
weight_increase_percent REAL,
echo_level_increase INTEGER NOT NULL DEFAULT 0,
eccentric_load_increase_percent INTEGER,
FOREIGN KEY (cycle_id) REFERENCES TrainingCycle(id) ON DELETE CASCADE
)""",
"""CREATE TABLE IF NOT EXISTS PlannedSet (
id TEXT PRIMARY KEY NOT NULL,
routine_exercise_id TEXT NOT NULL,
set_number INTEGER NOT NULL,
set_type TEXT NOT NULL DEFAULT 'STANDARD',
target_reps INTEGER,
target_weight_kg REAL,
target_rpe INTEGER,
rest_seconds INTEGER,
FOREIGN KEY (routine_exercise_id) REFERENCES RoutineExercise(id) ON DELETE CASCADE
)""",
"""CREATE TABLE IF NOT EXISTS CompletedSet (
id TEXT PRIMARY KEY NOT NULL,
session_id TEXT NOT NULL,
planned_set_id TEXT,
set_number INTEGER NOT NULL,
set_type TEXT NOT NULL DEFAULT 'STANDARD',
actual_reps INTEGER NOT NULL,
actual_weight_kg REAL NOT NULL,
logged_rpe INTEGER,
is_pr INTEGER NOT NULL DEFAULT 0,
completed_at INTEGER NOT NULL,
FOREIGN KEY (session_id) REFERENCES WorkoutSession(id) ON DELETE CASCADE,
FOREIGN KEY (planned_set_id) REFERENCES PlannedSet(id) ON DELETE SET NULL
)""",
"""CREATE TABLE IF NOT EXISTS ProgressionEvent (
id TEXT PRIMARY KEY NOT NULL,
exercise_id TEXT NOT NULL,
suggested_weight_kg REAL NOT NULL,
previous_weight_kg REAL NOT NULL,
reason TEXT NOT NULL,
user_response TEXT,
actual_weight_kg REAL,
timestamp INTEGER NOT NULL,
FOREIGN KEY (exercise_id) REFERENCES Exercise(id) ON DELETE CASCADE
)""",
"CREATE INDEX IF NOT EXISTS idx_cycle_day_cycle ON CycleDay(cycle_id)",
"CREATE INDEX IF NOT EXISTS idx_cycle_progress_cycle ON CycleProgress(cycle_id)",
"CREATE INDEX IF NOT EXISTS idx_planned_set_exercise ON PlannedSet(routine_exercise_id)",
"CREATE INDEX IF NOT EXISTS idx_completed_set_session ON CompletedSet(session_id)",
"CREATE INDEX IF NOT EXISTS idx_progression_event_exercise ON ProgressionEvent(exercise_id)"
)
else -> emptyList()
}
}

Copilot AI Jan 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hardcoded SQL migration statements in getMigrationStatements() duplicate the migration definitions from the .sqm files. This creates a maintenance burden - if migrations are updated, both locations must be changed. Consider reading the .sqm files directly or relying solely on SQLDelight's built-in migration mechanism with better error handling.

Copilot uses AI. Check for mistakes.
Comment on lines +73 to +74
msg.contains("already exists") ||
msg.contains("table .* already exists".toRegex())) {

Copilot AI Jan 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The regex pattern "table .* already exists" is created on every SQL statement execution inside a loop. For better performance, compile this regex once outside the loop or use a simple string contains check instead.

Suggested change
msg.contains("already exists") ||
msg.contains("table .* already exists".toRegex())) {
msg.contains("already exists")) {

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants