Skip to content

Conversation

@hermannakos
Copy link
Collaborator

No description provided.

kristofnemere and others added 30 commits September 22, 2025 11:25
# Conflicts:
#	apps/teacher/build.gradle
# Conflicts:
#	apps/parent/build.gradle
refs: MBL-19399
affects: Student
release note: Added new To Do List view for students to see upcoming assignments and tasks

* Feature skeleton and items UI.

* Fetching data, mapping UI items.

* DCP and today indicator.

* Sticky header.

* Text size changes.

* Added bottom bar badge.

* Pandas

* Complein status.

* Unit tests.

* Changed dates to the correct default.

* Added remote config flag.

* Handle correct context name and removed unused imports.

* Fixed RouterUtilsTest
* Optimize PR pipeline workflow with dynamic matrix and performance improvements

This commit significantly optimizes the GitHub Actions PR pipeline to reduce
build times and runner costs while improving developer experience.

Key optimizations:
- Dynamic matrix generation: Only builds apps mentioned in PR body, eliminating
  wasted runner time from jobs that skip all steps
- Enhanced Gradle caching: Added build cache and more granular cache keys for
  better hit rates (saves 2-5 minutes per build)
- Gradle performance flags: Enabled parallel execution, configuration cache,
  and optimized JVM settings (saves 3-7 minutes per build)
- Shallow clone: Added fetch-depth: 1 to all checkouts (saves 30-60 seconds)
- Removed unit-test dependency: UI/E2E tests now only depend on build job,
  allowing parallel execution (saves 5-10 minutes)
- Batch artifact uploads: Combined 3 separate uploads into 1 per app with
  compression and retention settings
- Firebase CLI caching: Cache npm global packages to speed up Firebase CLI
  installation (saves 30-45 seconds)
- Sticky QR code comments: Firebase distribution comments now update in place
  rather than creating duplicates, with timestamps and commit links
- Consolidated Firebase App ID setup: Replaced 3 conditional steps with 1
  case statement
- Fixed bugs: Removed jq dependencies, fixed e2e-tests job name, updated
  artifact download logic

Expected performance gains:
- Per PR (single app): 8-12 minutes faster
- Per PR (all 3 apps): 10-15 minutes faster
- Runner minute savings: 30-40% reduction
- Improved cache hit rates and faster subsequent builds

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

Co-Authored-By: Claude <noreply@anthropic.com>

* Fix hashFiles syntax error in pr-pipeline workflow

The hashFiles() function was incorrectly using multiple separate calls
which caused parsing errors in GitHub Actions. Consolidated all glob
patterns into a single hashFiles() call and simplified restore-keys.

This fixes the build failures where the workflow would fail with:
'hashFiles('**/*.gradle*, ...) failed. Fail to hash files under directory'

* Fix hashFiles glob patterns in workflow

Changes:
- Split invalid '**/*.gradle*' into explicit '**/*.gradle' and '**/*.gradle.kts'
- Changed 'gradle.properties' to '**/gradle.properties' to catch all instances
- Fixed buildSrc path from '**/buildSrc/**/*.kt' to 'apps/buildSrc/src/**/*.kt'

All patterns now match actual files in the repository:
- 24 .gradle files
- 8 .gradle.kts files
- 2 gradle-wrapper.properties files
- 3 gradle.properties files
- 4 Kotlin files in apps/buildSrc/src/

This resolves the hashFiles parsing error in GitHub Actions.

* Fix hashFiles to use separate calls for each pattern

GitHub Actions hashFiles() doesn't properly handle multiple comma-separated
patterns in a single call. Split into separate hashFiles() calls:
- hashFiles('**/*.gradle') for Groovy build files
- hashFiles('**/*.gradle.kts') for Kotlin build files

This approach avoids the parsing issues and generates proper cache keys.

* Use official Gradle hashFiles pattern from GitHub Actions docs

Using the exact pattern from actions/cache documentation:
hashFiles('**/*.gradle*', '**/gradle-wrapper.properties')

This is the recommended pattern for Gradle projects and should work
correctly with GitHub Actions' hashFiles function.

* Simplify hashFiles to single pattern for gradle-wrapper.properties

Use only gradle-wrapper.properties for cache key to avoid hashFiles errors.
This is the most critical file for Gradle cache invalidation as it
determines the Gradle version.

Testing with minimal pattern to isolate the hashFiles issue.

* Remove TimingsListener from student build.gradle

The TimingsListener is incompatible with Gradle's configuration cache
(--configuration-cache flag). Removing it allows us to use configuration
cache for faster builds.

The listener is no longer used and can be safely removed.

* Remove --configuration-cache flag from all Gradle commands

The project has multiple configuration cache incompatibilities:
- Task.project access at execution time in dataseedingapi/build.gradle
- Project serialization issues in various tasks
- Custom build listeners (TimingsListener was removed but issues remain)

Removing --configuration-cache to allow builds to succeed. We still get
benefits from --build-cache, --parallel, and other optimizations.

* Add caching, shallow clones, and Gradle performance tuning

Optimizations added:
1. Shallow git clones (fetch-depth: 1) - Faster checkout
2. Enhanced Gradle caching:
   - Cache Gradle packages (~/.gradle/caches, ~/.gradle/wrapper)
   - Cache Gradle build cache (build-cache-* and .gradle directory)
3. Gradle performance tuning flags:
   - --build-cache: Use Gradle build cache
   - --parallel: Run tasks in parallel
   - --max-workers=4: Limit parallel workers
   - --no-daemon: Disable daemon for CI
   - -Dorg.gradle.jvmargs="-Xmx4g": Increase heap size
   - -Dkotlin.compiler.execution.strategy=in-process: Faster Kotlin compilation

Also removed TimingsListener from student/build.gradle (incompatible with future config cache support)

* Split test matrix jobs into individual jobs for independent retry

Replace 3 matrix-based test jobs with 12 individual jobs:

Unit Tests (3 jobs):
- parent-unit-tests
- student-unit-tests
- teacher-unit-tests

UI Tests (6 jobs):
- parent-portrait-ui-tests
- parent-landscape-ui-tests
- student-portrait-ui-tests
- student-landscape-ui-tests
- teacher-portrait-ui-tests
- teacher-landscape-ui-tests

E2E Tests (3 jobs):
- parent-e2e-tests
- student-e2e-tests
- teacher-e2e-tests

Benefits:
- Each test job can be retried independently
- Flaky tests don't block other tests
- Better visibility in GitHub Actions UI
- Clearer dependencies (no matrix complexity)
- Easier debugging with explicit job names

All jobs include performance optimizations:
- Gradle flags: --build-cache, --parallel, --max-workers=4
- Shallow clones: fetch-depth: 1
- Enhanced caching for Gradle packages and build cache

* Fix hashFiles pattern in unit test jobs to use working syntax

* Update test job conditions: UI tests always run, E2E tests run when checkbox present in PR body

* Fix unit test Gradle command syntax - use multiline format

* Fix PR pipeline: unit tests, heap size, and sticky QR comments

- Fix unit test Gradle commands by using multiline format
- Increase JVM heap size to 6GB (matching apps/gradle.properties)
- Restore sticky QR code comments with hidden HTML identifiers
- Remove jq dependency with pure bash URL encoding
- Configure UI tests to run automatically on PR open/sync
- Configure E2E tests to run when "Run E2E test suite" is in PR body

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

Co-Authored-By: Claude <noreply@anthropic.com>

* Add security improvements to PR pipeline workflow

- Add file permissions (chmod 600) for sensitive files (keystore, service account)
- Add secret validation with clear error messages
- Add error handling for Firebase distribution operations
- Add URL extraction validation
- Add cleanup steps for sensitive files with always() condition
- Applied to build job and parent-portrait-ui-tests job

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

Co-Authored-By: Claude <noreply@anthropic.com>

* Complete security hardening for all test jobs

Apply security improvements to all remaining test jobs:
- Add secret validation with error messages for all test jobs
- Add chmod 600 for service account keys across all jobs
- Add cleanup steps with always() condition for all test jobs

Security improvements now applied to:
- 6 UI test jobs (portrait + landscape for parent/student/teacher)
- 3 E2E test jobs (parent/student/teacher)

All critical security issues from AI review are now resolved.

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

Co-Authored-By: Claude <noreply@anthropic.com>

* Enhance Claude code review workflow with update tracking and focused feedback

- On PR synchronize events, Claude now reads and updates its previous reviews instead of creating new ones
- Implemented checkbox-based progress tracking for identified issues
- Inline comments now reserved for change requests only; positive feedback stays in summary
- Added GitHub MCP tools for review management: list_reviews, get_review, list_review_comments, update_review_comment

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

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
…#3367)

Co-Authored-By: Claude noreply@anthropic.com

refs: MBL-19400
affects: Student
release note: Added swipe-to-complete and checkbox toggle functionality to To Do List with undo support

* Swipe actin UI.

* Swipe action

* Refactored viewModel injection and screen architecture so the content of the screen could be reused.

* Undo feature.

* Checkbox action.

* Offline and haptics.

* Corrected calendar user colors to be in sync with the to do.

* Fixed badge issues.

* Tests.

* Fixed remote config params for devDebugMinify build.

* Fade in/out text.

* Improve haptic feedback and fade-in animation for swipe gestures

- Apply ease-in cubic easing to swipe indicator fade-in for more natural animation
- Add GESTURE_START haptic feedback when user begins dragging
- Add GESTURE_END haptic feedback when swipe animation completes
- Keep TOGGLE_ON/OFF for checkbox interactions
- All haptics fall back to CONTEXT_CLICK on API < 34
- Provides better tactile feedback flow: start → drag → end

* Improved error logging.

* Add DefaultToDoListRouter for Teacher and Parent apps

- Created DefaultToDoListRouter with no-op implementations
- Added ToDoListRouter provider to Teacher ToDoModule
- Added ToDoListRouter provider to Parent ToDoModule
- Fixes build issues for Teacher and Parent apps since ToDoListFragment is in common code

* fixed tests

* CR fixes.

* Show snackbar for marking as undone as well.

* Test fixes.
refs: CLX-3127
affects: Horizon
release note: none
refs: MBL-17287
affects: Teacher
release note: Teachers can now reorder their dashboard cards by dragging them.
refs: CLX-3200
affects: Horizon
release note: none
Test plan:
- Unit tests pass (DashboardViewModelTest - 5 tests)
- Instrumentation tests pass (DashboardScreenTest - 4 tests)
- Feature flag toggles between old and new dashboard
- Pull-to-refresh works on new dashboard
- Drawer opens from toolbar hamburger menu
- Dark/light mode works correctly
- Landscape/tablet layout works correctly
- Accessibility checks pass

refs: MBL-19452
affects: Student
release note: Dashboard redesign infrastructure with Jetpack Compose foundation

## Checklist

- [ ] Tested in dark mode
- [ ] Tested in light mode
- [ ] Test in landscape mode and/or tablet
- [ ] A11y checked
- [ ] Approve from product
…ore submission types (#3365)

* Extend AssignmentDetailsInteractionTest to cover multiple submission types.

refs: MBL-17351
affects: Student
release note:

* Extend AssignmentDetailsInteractionTest to include additional media recording submission scenarios.

refs: MBL-17351
affects: Student
release note:

* Fix flaky audio submission test, also renames several media submission tests for better clarity.

refs: MBL-17351
affects: Student
release note:

* Add test asset files for submission tests

Adds test.txt, test_audio.mp3, and test_video.mp4 to androidTest assets directory. These files are required by AssignmentDetailsInteractionTest for testing file upload, audio, and video submission functionality. Without these files in the repository, the tests fail in CI with FileNotFoundException.

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

Co-Authored-By: Claude <noreply@anthropic.com>

* fix PR

refs: MBL-17351
affects: Student
release note:

* fix assets files

refs: MBL-17351
affects: Student
release note:

* Fix PR findings

refs: MBL-17351
affects: Student
release note:

---------

Co-authored-by: Claude <noreply@anthropic.com>
refs: MBL-19479
affects: Student, Parent
release note: Added support for displaying Submitted/NotSubmitted status for Discussion Checkpoint items.
…groups (#3373)

* [MBL-19500][Student] - Add expand/collapse functionality to conference groups

Add expand/collapse functionality to "New conferences" and "Concluded conferences" sections on the Conferences list page.

Changes:
- Add expand/collapse state tracking in ConferenceListModel
- Add HeaderClicked event handling
- Add expand icon to conference section headers
- Implement header click handlers to toggle section visibility
- Add icon rotation animation based on expanded state

Technical details:
- Both sections start expanded by default
- Sections expand/collapse independently
- Uses existing Mobius architecture pattern
- Added ic_expand_more drawable and a11y_expand_collapse string

refs: MBL-19500
affects: Student
release note: Added expand/collapse functionality to conference list sections

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

Co-Authored-By: Claude <noreply@anthropic.com>

* Update unit tests and apply linter improvements

Unit Test Updates:
- Updated ConferenceListPresenterTest to reflect changes to ConferenceHeader data class
  - Added headerType and isExpanded parameters to existing header assertions
  - Added 3 new test cases for collapse functionality:
    * Returns only header when new conferences section is collapsed
    * Returns only header when concluded conferences section is collapsed
    * Sections can be collapsed independently
  - All 12 tests passing

- Updated ConferenceListUpdateTest to test HeaderClicked event handling
  - Added 5 new test cases for header toggle behavior:
    * HeaderClicked with NEW_CONFERENCES toggles isNewConferencesExpanded (both directions)
    * HeaderClicked with CONCLUDED_CONFERENCES toggles isConcludedConferencesExpanded (both directions)
    * HeaderClicked only toggles the targeted section
  - All 11 tests passing

Linter Improvements:
- Applied automatic import organization
- Changed icon rotation to use animate() for smooth animation (200ms duration)
- Updated header layout to use existing ic_expand drawable with textDark tint
- Added explicit import for ConferenceHeaderType where needed

* Fix ConferenceListRenderTest tests (add new parameters).

* Delete unnecessary resource.

---------

Co-authored-by: Claude <noreply@anthropic.com>
* [MBL-19179][Parent] - Implement search in Grades page

Add a real-time search feature to the Parent app Grades page that allows filtering assignments by name.

Changes:
- Added search icon next to filter icon in Grades card
- Implemented collapsible search field below "Based on graded assignments" section
- Search filters assignments automatically when query reaches 3 characters
- Added real-time filtering as user types (no need to press enter)
- Enhanced SearchBar component with onQueryChange callback for instant text updates
- Added search state management in GradesUiState and GradesViewModel
- Filter logic removes empty assignment groups from results

Technical details:
- SearchBar now supports onQueryChange callback that fires on every keystroke
- ViewModel caches unfiltered items and applies filtering based on search query
- Case-insensitive search across assignment names
- Toggling search restores full unfiltered list

refs: MBL-19179
affects: Parent
release note: Added search functionality to Grades page

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

Co-Authored-By: Claude <noreply@anthropic.com>

* PR changes (animation, debounce, focusrequest)

* Minor PR fixes.

* PR changes. (Cursor keeps it's state).

---------

Co-authored-by: Claude <noreply@anthropic.com>
…box attachment (#3357)

* [MBL-19183][Student][Teacher] - Add loading indicator when opening inbox attachment

Add a loading dialog with app-specific theme colors when opening attachments from inbox.
This provides better user feedback during file loading operations.

Changes:
- Added loading dialog to Student and Teacher RouteMatcher when opening media attachments
- Dialog displays app-specific theme color (student purple or teacher purple)
- Properly manages dialog lifecycle (dismiss on load finished and loader reset)
- Updated dialog_loading_view.xml to allow programmatic color customization
- Removed hardcoded color override from CanvasLoadingView

refs: MBL-19183
affects: Student, Teacher
release note:

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

Co-Authored-By: Claude <noreply@anthropic.com>

* PR changes (null safety, and using imports instead of fully qualified names)

* Refactor the rest of the imports.

* Minor change. (put safety check in teacher's onLoadFinished method just like in the Student).

---------

Co-authored-by: Claude <noreply@anthropic.com>
refs: MBL-19402
affects: Student
release note: To Do List now supports navigation to items and Calendar dates.
refs: MBL-19401
affects: Student
release note: Added filtering options for To-Do list including personal to-dos, calendar events, completion status, favorite courses, and date ranges

* Filters UI

* Actions UI improvements.

* Implemented filters logic.

* Handle date filter change and other filter changed differently.

* Filter applied logic.

* Handle item removal when done is not shown.

* Fixed glitchy sticky header.

* Handle filters back press.

* Changed delay

* Added imports prompt to CLAUDE.md

* Tests

* Changes based on product/design input.

* Handle event/todo creation from the calendar

* Fixed unit tests

* Fixed conflicting tests and some PR comments.

* Fixed back handling and dialog style.

* Fixed tests

* Fixed tests

* Fixed QA findings.

* Fixed PR comments.
…tion (#3381)

Test plan:
1. Open the student app
2. Navigate to a course
3. Open the assignment list
4. Tap the filter button to open the filter screen
5. Press the back button (or use back gesture)
6. Verify that the filter screen closes and returns to the assignment list
7. Press back again to verify normal back navigation works from the list screen

refs: MBL-19511
affects: Student
release note: Fixed back button behavior in assignment list to close filter screen before navigating away

## Summary

Added a BackHandler to the AssignmentListScreen that intercepts back navigation when the filter screen is active. This ensures that pressing back from the filter screen returns to the assignment list instead of exiting the entire screen.

**Implementation Details:**
- Added `BackHandler` with `enabled` parameter set to `state.screenOption == AssignmentListScreenOption.Filter`
- When triggered, it calls `CloseFilterScreen` event to return to the list
- When filter is not active, default back navigation behavior is preserved

## Checklist

- [ ] Follow-up e2e test ticket created or not needed
- [ ] Tested in dark mode
- [ ] Tested in light mode
- [ ] Test in landscape mode and/or tablet
- [ ] A11y checked
- [ ] Approve from product
domonkosadam and others added 30 commits November 17, 2025 09:28
refs: CLX-3253
affects: Horizon
release note: none
… change (#3380)

## Summary

Fixes filter state persistence on orientation changes in the Teacher app People page and prevents crashes when rotating device with filter dialog open.

## Test plan

1. Open Teacher app and navigate to a course's People page
2. Tap the filter icon and select one or more sections/groups
3. Verify the filter is applied and the list shows only filtered users
4. Rotate the device (portrait ↔ landscape)
5. ✅ Verify filter remains active, UI shows correct filter labels, and data stays filtered
6. Tap the X button to clear the filter
7. Rotate the device again
8. ✅ Verify filter stays cleared (shows "All People")
9. Open the filter dialog and rotate the device while dialog is open
10. ✅ Verify no crash occurs and dialog remains functional

refs: MBL-19488

affects: Teacher

release note: Fixed an issue where People page filters would reset after rotating the device

## Checklist

- [x] Follow-up e2e test ticket created or not needed
- [x] Tested in dark mode
- [x] Tested in light mode
- [x] Test in landscape mode and/or tablet
- [ ] A11y checked
- [ ] Approve from product
This PR implements the Course Invitations widget for the Student Dashboard, allowing students to view and manage their pending course invitations.

## Summary

- Added data layer for loading and handling course invitations
- Implemented CourseInvitationsViewModel with optimistic updates and error handling
- Created CourseInvitationsWidget UI with horizontal pager for multiple invitations
- Integrated widget into Dashboard with snackbar support
- Added comprehensive unit and integration tests

## Changes

### Data Layer
- Added `CourseInvitation` domain model
- Added `LoadCourseInvitationsUseCase` to fetch pending invitations
- Added `HandleCourseInvitationUseCase` to accept/decline invitations
- Added Room database entities and DAOs for offline support

### ViewModel
- Implemented `CourseInvitationsViewModel` with loading, error, and success states
- Added optimistic updates for accept/decline actions
- Added error handling with rollback and retry functionality
- Added snackbar state management

### UI
- Created `CourseInvitationsWidget` with horizontal pager support
- Added multi-column layout support for tablets (1 column on compact, 2 on medium, 3 on expanded)
- Added decline confirmation dialog
- Integrated with Dashboard snackbar system
- Added proper theme support for light/dark modes

### Testing
- Added unit tests for ViewModels (CourseInvitationsViewModel, DashboardViewModel)
- Added integration tests for CourseInvitationsWidget with pager navigation
- Test coverage includes loading states, error handling, callbacks, and dialog behavior

## Test Plan

1. **Display Invitations**
   - Launch Student app and navigate to Dashboard
   - Verify Course Invitations widget appears when there are pending invitations
   - Verify invitation cards show course names correctly
   - Test with multiple invitations to verify paging works correctly based on screen size

2. **Accept Invitation**
   - Tap "Accept" button on an invitation
   - Verify invitation is removed from the widget immediately
   - Verify success snackbar appears with course name
   - Verify course appears in course list

3. **Decline Invitation**
   - Tap "Decline" button on an invitation
   - Verify confirmation dialog appears
   - Tap "Decline" in dialog to confirm
   - Verify invitation is removed from widget
   - Verify success snackbar appears

4. **Cancel Decline**
   - Tap "Decline" button on an invitation
   - Tap "Cancel" in confirmation dialog
   - Verify invitation remains in widget

5. **Error Handling**
   - Test with network offline
   - Attempt to accept/decline an invitation
   - Verify invitation is restored in widget
   - Verify error snackbar appears with "Retry" action
   - Tap "Retry" and verify action is attempted again

6. **Pager Navigation**
   - With more invitations than columns, swipe left/right between pages
   - Verify pager indicator appears when there are multiple pages
   - Verify smooth transitions between pages
   - Test on different screen sizes (phone: 1 column, tablet portrait: 2 columns, tablet landscape: 3 columns)

7. **Widget Visibility**
   - Verify widget does not appear when there are no pending invitations
   - Verify widget does not appear when loading fails
   - Accept/decline all invitations and verify widget disappears

refs: MBL-19456
affects: Student
release note: Added Course Invitations widget to Student Dashboard

## Checklist

- [ ] Follow-up e2e test ticket created or not needed
- [ ] Tested in dark mode
- [ ] Tested in light mode
- [ ] Test in landscape mode and/or tablet
- [ ] A11y checked
- [ ] Approve from product

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

## Summary
This PR fixes a `NullPointerException` crash that occurred when users attempted to download attachments with a null URL. The crash was happening in the Inbox Compose screen when users clicked on certain attachments.

## Changes
- Added null check in `FileDownloader.downloadFileToDevice()` before parsing the URL
- Display user-friendly error toast when URL is null instead of crashing
- Prevents crash at `FileDownloader.kt:40` where `Uri.parse()` was called with null

## Crash Details
- **Exception**: `NullPointerException: uriString`
- **Location**: `FileDownloader.kt:40` (`Uri.parse(downloadURL)`)
- **Trigger**: User clicks download on attachment with null URL in Inbox
- **Firebase Issue**: `b4aefef80929c2c5264d99e600674a1d`
- **Version**: 8.3.0 (282)

## Root Cause
The `Attachment` model's `url` field is nullable (`String?`), but the download code didn't handle the null case. When an attachment had a null URL (which can occur in certain edge cases), the app would crash when trying to parse it.

## Test Plan
1. Build and install the Student app
2. Navigate to Inbox and compose a message with attachments
3. Try to download an attachment with a null URL (edge case scenario)
4. Verify app shows error toast instead of crashing
5. Verify normal attachments with valid URLs still download correctly
6. Repeat for Parent app

refs: MBL-19522
affects: Student, Parent
release note: Fixed crash when attempting to download attachments with invalid URLs.

## Checklist

- [x] Follow-up e2e test ticket created or not needed
- [x] Tested in dark mode
- [x] Tested in light mode
- [x] Test in landscape mode and/or tablet
- [ ] A11y checked
- [ ] Approve from product

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

Fixed SQLiteException crash occurring in production when FileSyncProgressEntity table is missing due to corrupted database migrations. This crash was affecting users who enable offline sync features.

## Test Plan

1. Enable offline sync in the app
2. Sync a course with files
3. Verify sync progress displays correctly without crashes
4. Check Firebase Crashlytics to confirm SQLiteException reports decrease

Note: Users with already-corrupted databases will need to clear app data or reinstall, but the app will no longer crash - it will gracefully degrade by hiding sync progress UI.

## Changes

- Added `fallbackToDestructiveMigration()` to OfflineDatabaseProvider to recreate database on migration failures
- Added try-catch error handling around all DAO queries in AggregateProgressObserver (courseSyncProgressDao, fileSyncProgressDao, studioMediaProgressDao)
- Removed duplicate KAPT configuration from pandautils build.gradle (leftover from KSP migration)
- All exceptions are reported to Firebase Crashlytics for monitoring

refs: MBL-19523
affects: Student
release note: Fixed crash when using offline sync features
refs: CLX-3245
affects: Horizon, Student
release note: none
refs: CLX-3254
affects: Student
release note: none
refs: MBL-19406
affects: Student
release note: Internal test infrastructure improvements - no user-facing changes
…signments (#3403)

## Summary

Fixes a floating-point precision issue where letter grades would incorrectly round down when selecting grades from the dropdown in SpeedGrader. This was particularly noticeable with 1-point assignments where selecting "A" (90%) would incorrectly change to "B" after the grade was applied.

## Problem

The issue occurred because:
1. When selecting "A" (90% = 0.9), the percentage was converted to a score
2. Float/Double conversions introduced precision errors (0.9 -> 0.8999999...)
3. The comparison `(0.8999999 >= 0.9)` would fail, dropping to the next grade

## Solution

Added an epsilon tolerance (0.0000001) to the grade comparison in `convertPercentScoreToLetterGrade()` to account for floating-point precision loss during conversions.

## Test Plan

Manual testing:
1. Create a 1-point assignment with Letter Grading enabled
2. Open SpeedGrader in Teacher app
3. Select "A" grade from the dropdown
4. Verify grade remains "A" and doesn't automatically change to "B"
5. Test with other letter grades (B, C, D) to ensure they also work correctly

Test credentials are available in the ticket.

Unit tests:
- Added 4 new test cases specifically for floating-point precision with 1-point assignments
- All 20 unit tests in `ModelExtensionsTest` pass

refs: MBL-19540
affects: Teacher
release note: Fixed an issue where letter grades in SpeedGrader would incorrectly round down to the next lower grade, especially for low-point assignments
Implements a new Institutional Announcements Widget for the Student dashboard that displays account notifications with institutional branding.

## Implementation Details

### New Features
- **Institutional Announcements Widget** - Displays up to 5 most recent announcements sorted by date
- **Account Branding** - Shows institution logo with fallback to 3-letter abbreviation on brand-colored background
- **Icon Badges** - Notification type indicators (warning, error, info, calendar, question) with solid icons
- **Horizontal Paging** - Swipe through announcements with page indicators
- **Multi-Column Support** - Adapts to tablet layouts with configurable columns
- **Widget Title** - "Announcements (x)" format showing count of announcements

### Architecture
- **Repository Layer** - `UserRepository` for account data, `AccountNotificationRepository` for notifications
- **Use Case** - `LoadInstitutionalAnnouncementsUseCase` combines data from both repositories, sorts by date, limits to 5
- **ViewModel** - `InstitutionalAnnouncementsViewModel` with StateFlow-based UI state management
- **Widget** - Jetpack Compose UI with Material Design 3 components

### Design
- Square logo with 8dp rounded corners (changed from circular)
- Icon badge positioned at top-start corner with 8dp offset (50% overlap)
- Solid icon variants with appropriate color tinting (warning=textWarning, error=textDanger, others=textInfo)
- Widget hides when loading, on error, or when no announcements available
- Announcement card shows: institution name, date/time, subject, message preview

### Testing
- **31 unit tests** covering repository, use case, and view model layers
- **9 instrumentation tests** covering UI states, paging, clicks, and multi-column layout
- All tests passing on Pixel 2 API 29 emulator

Test plan:
1. Log in to Student app with an account that has institutional announcements configured
2. Navigate to Dashboard
3. Verify Institutional Announcements Widget appears between Course Invitations and Welcome widgets
4. Verify widget title shows "Announcements (x)" where x is the number of announcements
5. Verify institution logo is displayed as a square with rounded corners
6. Verify icon badge appears at top-start corner of logo with appropriate color
7. If multiple announcements exist, swipe left/right to view different announcements
8. Verify page indicators appear and update when swiping
9. Tap on an announcement to verify it opens the announcement details
10. Test in landscape/tablet mode to verify multi-column layout
11. Test with accounts that have no announcements - widget should not be displayed

refs: MBL-19457
affects: Student
release note: Students can now view institutional announcements on their dashboard

## Checklist

- [ ] Follow-up e2e test ticket created or not needed
- [x] Tested in dark mode
- [x] Tested in light mode
- [x] Test in landscape mode and/or tablet
- [ ] A11y checked
- [ ] Approve from product
refs: CLX-3244
affects: Student
release note: none
…gs (#3404)

## Summary
Implements the Welcome Widget for the Student app dashboard with time-based personalized greetings and motivational messages.

**Key Features:**
- Time-based greetings (Morning 4am-12pm, Afternoon 12pm-5pm, Evening 5pm-9pm, Night 9pm-4am)
- Personalized with user's first name
- 88 motivational messages (52 generic + 36 time-specific)
- Pull-to-refresh updates greeting and message
- Full TalkBack accessibility support
- Comprehensive unit test coverage (37 tests)

**Architecture:**
- Implemented in `pandautils` for cross-app reusability
- MVVM with Use Cases pattern
- Hilt dependency injection
- Testable design with TimeProvider abstraction

## Test Plan
1. Log in to the Student app
2. Navigate to Dashboard
3. Verify Welcome Widget displays with time-appropriate greeting and user's first name
4. Pull down to refresh the dashboard
5. Verify the motivational message changes
6. Test at different times of day to verify greeting changes:
   - 4am-12pm: "Good morning"
   - 12pm-5pm: "Good afternoon"
   - 5pm-9pm: "Good evening"
   - 9pm-4am: "Good night"
7. Enable TalkBack and verify widget is accessible
8. Run unit tests: `./gradle/gradlew -p apps :pandautils:testDebugUnitTest --tests "com.instructure.pandautils.features.dashboard.widget.welcome.*"`

refs: MBL-19455
affects: Student
release note: Added personalized welcome widget with time-based greetings and motivational messages

## Checklist
- [x] Unit tests added and passing (37 tests, 4 test suites)
- [x] Dark/light mode compatible (uses theme colors)
- [x] Accessibility support (TalkBack content descriptions)
- [x] Tablet/landscape layout responsive
- [x] Deployed and manually tested on device
refs: CLX-3203
affects: Student
release note: none
refs: CLX-3201
affects: Student
release note: None

---------

Co-authored-by: domonkosadam <domonkosadam01@gmail.com>
…#3409)

## Summary
After orientation change in the submission details page, the webview's dark mode state was not preserved, causing the theme to appear incorrect.

## Issue
The problem occurred because:
- The ViewModel is retained across configuration changes
- `LoadWebView` action was only sent once during ViewModel initialization
- When the Fragment was recreated after rotation, webview state was restored from `savedInstanceState`
- However, `enableAlgorithmicDarkening()` was never called again, so the dark mode setting was lost

## Solution
Fixed by calling `enableAlgorithmicDarkening()` after restoring webview state from `savedInstanceState` in `onViewCreated()`. This ensures the dark mode setting is reapplied on every configuration change.

## Test Plan
1. Open Parent app and sign in
2. Navigate to a course with assignments
3. Open an assignment with a submission
4. Tap to view submission details (opens webview)
5. Rotate device to change orientation
6. Verify webview dark mode is preserved correctly after rotation

refs: MBL-19489
affects: Parent
release note: Fixed issue where submission details page would change theme after rotating device

- [x] Verified on physical device (Samsung SM-A546B)
- [x] Tested orientation changes
- [x] Verified dark mode state preservation
refs: MBL-19515
affects: Student
release note: Fixed errors when viewing submissions in courses across different Canvas shards.
…rage

refs: MBL-19553
affects: Student
release note: none
## Summary

Adds automated unit test result reporting as a sticky comment on pull requests.

## Test Plan

1. Open a PR that affects Student, Teacher, or Parent apps
2. Wait for unit tests to complete
3. Verify a comment appears with unit test results
4. Push another commit to the PR
5. Verify the same comment updates (doesn't create a new one)
6. Check that all modules are reported correctly

refs: none
affects: Student, Teacher, Parent
release note: none

## Changes

- Modified `.github/workflows/pr-pipeline.yml` to:
  - Upload test result artifacts from all unit test jobs (Parent, Student, Teacher, Horizon, Submodules)
  - Add new `unit-test-report` job that parses test results and posts/updates a PR comment

## Features

- 🧪 **Comprehensive Coverage**: Reports results from all apps and modules
- 📊 **Clear Summary**: Shows test counts, failures, duration, and success rate
- 🔄 **Sticky Comment**: Updates the same comment on each push (no spam)
- ✅ **Status Indicators**: Visual emoji indicators for pass/fail/warning states
- 🛡️ **Robust**: Runs even if tests fail or are skipped, handles missing data gracefully
- ⏱️ **Timestamp**: Shows when results were last updated

## Example Output

The comment will look like:

```
🧪 Unit Test Results

✅ Student App
- Tests: 1,234 total, 0 failed, 0 skipped
- Duration: 2m 15s
- Success Rate: 100%

✅ Teacher App
- Tests: 987 total, 0 failed, 0 skipped
- Duration: 1m 48s
- Success Rate: 100%

✅ Parent App
- Tests: 456 total, 0 failed, 0 skipped
- Duration: 1m 12s
- Success Rate: 100%

---
Summary
- Total Tests: 2,677
- Failed: 0
- Skipped: 0
- Status: ✅ All tests passed!

Last updated: Wed, 27 Nov 2025 10:45:00 GMT
```
* Fix custom statuses e2e tests in student and parent (use global variable).

refs: MBL-19566
affects: Student, Teacher, Parent
release note:

* Add logging to check why custom statuses has not cleared properly.

* Stub breaking Vanity Domain E2E (Cause: SSL HandshakeException).
refs: CLX-3202
affects: Student
release note: Career - Redesigned notebook experience.

---------

Co-authored-by: Claude <noreply@anthropic.com>
…ssignments

refs: MBL-19559
affects: Student
release note: Fixed issue where students couldn't view New Quizzes submissions after the assignment's until date had passed
refs: MBL-19497
affects: Student
release note: Fixed an issue where bookmarks created from notifications or the ToDo list would fail to open
…ng (#3411)

## Summary

Fixes critical bug where student file submissions appeared in Canvas Submissions folder but not in SpeedGrader. This occurred when files uploaded successfully but the final submission API call failed silently due to network issues.

## Changes

### Database Layer (Phase 1)
- Add `SubmissionState` enum with 7 states tracking submission lifecycle: QUEUED, UPLOADING_FILES, SUBMITTING, VERIFYING, COMPLETED, FAILED, RETRYING
- Add 5 new tracking columns to `CreateSubmissionEntity`:
  - `submission_state` (tracks current state)
  - `state_updated_at` (timestamp)
  - `retry_count` (attempt counter)
  - `last_error_message` (error details)
  - `canvas_submission_id` (Canvas ID after success)
- Create backward-compatible database migration (v6 → v7)
- Add DAO methods: `updateSubmissionState()`, `incrementRetryCount()`, `setCanvasSubmissionId()`, `findSubmissionsByState()`

### Worker & Error Handling (Phase 2)
- Add exponential backoff policy to WorkManager (30s initial delay, exponential growth)
- Implement intelligent retry logic in `SubmissionWorker`:
  - Network 5xx errors → retry up to 3 times
  - Connectivity issues → retry
  - Exceptions → retry
  - Authorization 4xx errors → permanent failure
- Update submission states throughout upload lifecycle
- Store Canvas submission ID and error messages

### UI Foundation (Phase 3)
- Update `BaseSubmissionHelper` methods to return submission IDs instead of Unit
- Enable future UI tracking of submission progress
- All changes backward compatible with existing UI

## Test Plan

### Manual Testing
1. Enable airplane mode or use network throttling
2. Create an assignment requiring file submission
3. As a student, upload files to the assignment
4. Observe files upload successfully
5. While "Submitting" notification shows, disconnect network
6. Verify WorkManager automatically retries submission (check logs for retry attempts)
7. Reconnect network
8. Verify submission completes successfully and appears in both:
   - Student's Submissions folder in Canvas
   - Teacher's SpeedGrader

### Database Migration Testing
1. Install previous version of app with existing submissions
2. Install this build
3. Verify database migrates successfully (v6 → v7)
4. Verify existing submissions still work
5. Create new submission and verify new fields populated

### Edge Cases
1. Test with process death during upload (force stop app)
2. Test with app backgrounded during submission
3. Verify submissions persist and resume correctly
4. Test permanent failures (invalid auth) don't retry indefinitely

refs: MBL-19546
affects: Student
release note: Fixed issue where file submissions occasionally didn't appear in SpeedGrader after successful upload

- [x] Dark/light mode testing - N/A (no UI changes)
- [x] Landscape/tablet testing - N/A (no UI changes)
- [x] Accessibility testing - N/A (no UI changes)
- [ ] Product approval - Bug fix, no product changes

🤖 Generated with [Claude Code](https://claude.com/claude-code)
refs: MBL-19567
affects: Student
release note: Updated default time period for ToDo list to show items from 4 weeks ago through this week.
…#3410)

## Summary

When a user sends a message or reply, the inbox list screen should display the newly sent message immediately. However, the cache was not being invalidated before refreshing, causing stale data to appear (especially noticeable in the SENT scope).

This fix ensures the cache is properly cleared before refreshing the inbox list, following the existing pattern already established in the `conversationUpdated()` method.

## Test Plan

1. Open the Canvas app and navigate to Inbox
2. Send a new message to any recipient
3. Navigate to the SENT scope
4. Verify the newly sent message appears immediately in the list
5. Switch between different scopes (INBOX, UNREAD, STARRED, SENT, ARCHIVED)
6. Verify each scope shows fresh data without cached stale entries
7. Test replying to an existing conversation and verify the conversation list updates

refs: MBL-19490
affects: Student, Teacher, Parent
release note: Fixed an issue where newly sent messages would not appear immediately in the inbox

- [x] Dark/light mode testing
- [x] Landscape/tablet testing
- [x] Accessibility testing
- [ ] Product approval (if needed)
#3420)

## Summary

Adds a "Retry" parameter to all Pendo submission tracking events (both SUCCEEDED and FAILED) to distinguish between first-time submission attempts and retry attempts after failures. This enables product managers to accurately analyze submission success rates and understand whether high failure counts include multiple retry attempts from the same user.

## Changes

- Added `RETRY` constant to `AnalyticsParamConstants` in canvas-api-2 library
- Updated all 13 submission analytics events in `SubmissionWorker` to include retry parameter:
  - Value = 1 if `retryCount > 0` (retry attempt)
  - Value = 0 if `retryCount == 0` (first attempt)
- Leverages existing `retryCount` field from `CreateSubmissionEntity` added in MBL-19546

## Events Updated

All submission analytics events now include the retry parameter:
- `SUBMIT_MEDIARECORDING_SUCCEEDED` / `FAILED` (3 events)
- `SUBMIT_FILEUPLOAD_SUCCEEDED` / `FAILED` (2 events)
- `SUBMIT_TEXTENTRY_SUCCEEDED` / `FAILED` (2 events)
- `SUBMIT_URL_SUCCEEDED` / `FAILED` (2 events)
- `SUBMIT_STUDIO_SUCCEEDED` / `FAILED` (2 events)
- `SUBMIT_ANNOTATION_SUCCEEDED` / `FAILED` (2 events)

## Test Plan

### Manual Testing
1. Create an assignment requiring any submission type (text, file, URL, etc.)
2. As a student, submit to the assignment
3. Verify submission succeeds and check Pendo analytics event includes `retry: 0`
4. Simulate a network failure during submission (airplane mode or network throttling)
5. Retry the submission after the failure
6. Verify the retry attempt includes `retry: 1` in the Pendo analytics event

### Verification
- Pendo analytics events for successful first-time submissions should have `retry: 0`
- Pendo analytics events for retry attempts (after WorkManager retries) should have `retry: 1`
- All 13 submission events should include the retry parameter

refs: MBL-19563
affects: Student
release note: Improved analytics tracking for submission attempts to better distinguish between first attempts and retries

- [ ] Dark/light mode testing - N/A (analytics only, no UI changes)
- [ ] Landscape/tablet testing - N/A (analytics only, no UI changes)
- [ ] Accessibility testing - N/A (analytics only, no UI changes)
- [ ] Product approval - Not required (analytics enhancement)

🤖 Generated with [Claude Code](https://claude.com/claude-code)
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.

9 participants