From 923c0ad324c9e5e3dfab7e40e145ff8038317ebc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Miguel=20Rubio?= Date: Tue, 30 Apr 2024 12:11:53 +0200 Subject: [PATCH] Update develop 2.10 (#3618) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Create publish-libraries.yml * Create build-release-candidate.yml * Update build-release-candidate.yml * Update mobile-ui library version to 0.1-SNAPSHOT (#3579) Signed-off-by: andresmr * Release/2.10 (#3611) * Androapp 5724 remove organization unit in tei event ds card if user only has access to one org unit (#3459) * add logic to show organisation unit organisation unit in TEI list, DataSet and tracker program list will only be shown when user has access to more than one organisation unit. * fix lint error * move `displayOrgUnitName` logic to ui layer --------- Co-authored-by: Siddharth Agarwal * [ANDROAPP-5887] update SDK (#3471) * feat: [ANDROAPP-5830] update mobile UI version to 0.2-20240103.112017-2 (#3461) * fix: [ANDROAPP-5830] update mobile-ui version 0.2-20240103.112017-2 * feat: [ANDROAPP-5830] add to do's and bare minimun for compilation * ANDROAPP-5829-adapt-input-image-to-new-mobile-ui-functionality (#3462) * Handle `Intent.ACTION_SEND` in form view for sharing files * Open chooser intent when share button is clicked in `ImageInput` * Open chooser intent when share button is clicked in `InputSignature`` * Move `getBitmap` extension to commons module * Move `FormFileProvider` to commons module * Add image detail activity Currently we don't have a working "save image" functionality. So, it didn't make sense to copy the implementation that is not working. So for the time being I marked it as TODO * Launch `ImageDetailActivity` instead of `ImageDetailBottomDialog` * Remove `ImageDetailBottomDialog` * feat: [ANDROAPP-5848] Adapt Input dropdown to new functionality (#3465) * feat: [ANDROAPP-5848] remove old option set dialog, recycler event and deprecated code. refactor OptionSetConfiguration to default adapt input dropdown to new functionality * feat: [ANDROAPP-5848] remove deprecated code * fix: [ANDROAPP-5848] remove deprecated code * fix: [ANDROAPP-5848] ktlint fix * fix: [ANDROAPP-5848] dismiss keyboard on dismiss popup * fix: [ANDROAPP-5848] add old pop in again to maintain old form logic * feat: [ANDROAPP-5848] update mobile ui to version 0.2-20240123.112704-7 * fix: [ANDROAPP-5848] sonar fix * fix: [ANDROAPP-5848] sonar fix --------- Co-authored-by: Sasikanth Miriyampalli * [ANDROAPP-5891] Implement new signature for compose form and maintain current for old one. (#3475) * feat: [ANDROAPP-5568] hide program stage behavior (#3464) * feat: [ANDROAPP-5523] change error access message at home (#3467) * [Update develop with main] conflict resolution * [Update develop with main] fix test * [Update release notes] Correct typos and delete unnecessary release.info file (#3480) * [ANDROAPP-5760] DashboardRepositoryImpl refactor to kotlin, new test implementation start * Move shouldSuccessfullyCreateANewEvent to unit test * Move shouldNotBeAbleToCreateNewEventsWhenFull to unit tests * Move shouldSuccessfullySyncAChangedTEI to unit tests * fix test Signed-off-by: Pablo * fix test Signed-off-by: Pablo * [ANDROAPP-5645] Close enrollment data section if already completed (#3484) --------- Signed-off-by: Pablo * feat: [ANDROAPP-5683] legend description (#3486) * fix: [ANDROAPP-5900] adapt Input providers to new TextFieldValue usage * fix: [ANDROAPP-5900] update mobile ui 0.2-20240206.071329-16 * fix: [ANDROAPP-5900] set initial selection to value length * updates paging on search to paging3 * update search tests * fix code smells * updates sdk and prevents scroll to top when paginating * Updates sdk * update and adapt design library version * fix tests * adds helpertext to table's input field (#3487) * show confirmation dialog show confirmation dialog before deleting TEI or removing enrolments * show/hide menu items Show TEI Dashboard menu items based on correspondent authorities * update confirm delete dialog design Older BottomSheetDialog has now been replaced by mobile-ui library `BottomSheetSheel` component * Set button color style in delete bottom sheet dialog * Run code formatting * Fix delete button callback not working in `DeleteBottomSheetDialog` * [ANDROAPP-5894] Adapt enrollment label to be configurable from server (#3493) Signed-off-by: Pablo * Adds a no connection listener to Sync Dialog to show snackbar to users * resolve code smell. now dialog uses SAM interface for the NoConnectionListener * feat: [ANDROAPP-5896] configurable event label (#3495) * cache some complex operations (#3494) Signed-off-by: Pablo * [ANDROAPP-5857] image download implementation (#3498) * download image files to external directory Signed-off-by: Pablo * download qr/barcode files to external directory Signed-off-by: Pablo * show message when file is downloaded Signed-off-by: Pablo --------- Signed-off-by: Pablo * feat: [ANDROAPP-5802] schedule events after completion (#3483) * update: [ANDROAPP-4826] design system version updated * add: [ANDROAPP-4826] String added * add: [ANDROAPP-4826] Infobar added, warning message field added * update: [ANDROAPP-4826] visual changes when there is no fields in the section * update: [ANDROAPP-4826] code formatted, visual changes to description * delete: [ANDROAPP-4826] Dummy section deleted in FormSectionMapper * update: [ANDROAPP-4826] the InfoBar doesn't appears when the form is loading * update: [ANDROAPP-4826] libs updated * [ANDROAPP-4826] Show no fields warnings if one section and is NO_HEADER * [ANDROAPP-4826] Refactor section warning generation to mapper * [ANDROAPP-4826] Refactor section warning generation to mapper * [ANDROAPP-5901] Move create button in timeline view (#3490) * [ANDROAPP-5800] Replace xml view by ComposeView * [ANDROAPP-5800] Set up SearchParameter structure * [ANDROAPP-5800] Implement SearchParametersRepository * [ANDROAPP-5800] update search query * [ANDROAPP-500] Remove deprecated steps in walkthrough * [ANDROAPP-500] Implement search and clear button * [ANDROAPP-500] Implement SnackBar for min attributes search * [ANDROAPP-500] Refactor search and clear button to SearchTEIViewModel * [ANDROAPP-500] rebase develop and update library * [ANDROAPP-500] show min attributes message * [ANDROAPP-5800] handle focus * [ANDROAPP-5800] update design library * [ANDROAPP-5800] clear search parameters * [ANDROAPP-5800] Use fieldUiModel for parameter provider * [ANDROAPP-5800] insert inputstyle as parameter * [ANDROAPP-5800] text range to textInput model * [ANDROAPP-5800] update display name values * [ANDROAPP-5800] show org unit selector * [ANDROAPP-5800] remove SearchRepository.kt * [ANDROAPP-5800] fix tests * [ANDROAPP-5800] fix tests * [ANDROAPP-5800] Add composeTestRule to functional tests * [ANDROAPP-5800] Update design library version * [ANDROAPP-5800] Remove SearchParametersRepository.kt and use SearchRepositoryKt * [ANDROAPP-5800] provide icons based on valueType * [ANDROAPP-5800] Implement QR and Barcode scanning * [ANDROAPP-5800] fix code smells * [ANDROAPP-5800] clear focus on clear search * [ANDROAPP-5800] close keyboard om search and clear * [ANDROAPP-5800] close empty fields when clear search * [ANDROAPP-5800] request focus when item is opened * [ANDROAPP-5800] move focus to next item * [ANDROAPP-5800] enable disable search button on empty parameters * [ANDROAPP-5805] clear search button visibility * [ANDROAPP-5805] Adapt top bar when searching * [ANDROAPP-5805] Add replay to mutable search flow in order to emit the first value * [ANDROAPP-5805] Remove connected rounded corners on landscape * [ANDROAPP-5805] update ProgramEventTest to remove check on org unit when is not visible * [ANDROAPP-5800] configure toolbar for landscape * [ANDROAPP-5800] remove unused program variable * [ANDROAPP-5800] remove needs force update parameter from Form only used for search * [ANDROAPP-5800] Remember ScanContract on SearchParametersScreen * [ANDROAPP-5800] Clear focus when closing search * [ANDROAPP-5800] Avoid double updating Age field * [ANDROAPP-5800] Not loop on next click when gets the last item * [ANDROAPP-5800] set rounded corners on show filters landscape * [ANDROAPP-5800] fix code smell * feat: [ANDROAPP-5794] Date Selectors (#3501) * feat: [ANDROAPP-5794] Implement new InputDate Date and TimePicker functionalities * feat: [ANDROAPP-5794] add selectable dates and refactor code * feat: [ANDROAPP-5794] update design system version * feat: [ANDROAPP-5794] update input date in scheduling dialog * feat: [ANDROAPP-5794] adapt new design for implementations with periods * feat: [ANDROAPP-5794] remove old calendar from event details * feat: [ANDROAPP-5794] fix test and add year range control * feat: [ANDROAPP-5794] fix ui test and reformat default values * feat: [ANDROAPP-5794] sonar fix * feat: [ANDROAPP-5794] ktlint * feat: [ANDROAPP-5794] ktlint * feat: [ANDROAPP-5794] update date value on reset button click * fix: [ANDROAPP-5802] scheduling dialog not displayed (#3507) * [ANDROAPP-1540] Multi selection for option set in programs (#3500) Signed-off-by: Pablo * fix(translations): sync translations from transifex (develop) WARNING: This automated sync from transifex removed more lines than it added. Please check carefully before merging! * fix(translations): sync translations from transifex (develop) * fix: CI fix for new date picker selection (#3511) * fix: CI fix for new date picker selection * fix: readapt to avoid flakiness * fix: remove log printing for datepicker * fix: ignore test * [ANDROAPP-4457] Configurable basemaps (#3509) Signed-off-by: Pablo * Update ActivityTestRule on SyncActivityTest * Add function to get single tracked entity in `SearchRepositoryKt` * Add `OnQrCodeScannedFormIntent` * Return `OnQrCodeScanned` instead of `OnSave` when QR code is scanned * Update `SearchRepositoryKt#trackedEntity` function to return list of TEIs * Open TEI dashboard when a TEI is found after QR code scanning * Update ActivityTestRule on ProgramEventTest * feat: [ANDROAPP-5919] multiselection on dataset * update design library * Grouped stage events ui logic updated * show all event stages * add toggle button to grouped stage events * update show logic for timeline events * fix code styling * fix toggle button click Earlier when clicking on a particular stage show more buttons, all the stage events were expanding. This commit fixes this. * update toggle button design * update stage item ui * update design for timeline and grouped events * fix lint error * fix roboto test * fix ui test for completed events * fix click for event and stages item * fix `SEARCHTETEST` * fix roboto test * ignore depreciated test * Ignore test WIP * fix sonar test * fix lint error * fix left margin for event card * remove data element value null or empty value * fix text style for stage item title and descr * fix color for skipped element * fix metadata avatar for stage and event item * fix lint error * fix test notation * [ANDROAPP-5941] Use multiplatform rule engine (#3505) * Rename .java to .kt Signed-off-by: Pablo * update rule engine Signed-off-by: Pablo * update rule engine Signed-off-by: Pablo * Rename .java to .kt Signed-off-by: Pablo * use multiplatform rule engine Signed-off-by: Pablo * rule engine module Signed-off-by: Pablo * set null enrollment uid Signed-off-by: Pablo * fix dagger Signed-off-by: Pablo * fix tests Signed-off-by: Pablo * fix LMIS test Signed-off-by: Pablo * ktlint Signed-off-by: Pablo * fix tests Signed-off-by: Pablo * add desugar lib Signed-off-by: Pablo * removed unused test Signed-off-by: Pablo * fix code smells Signed-off-by: Pablo * remove duplicated dependencies Signed-off-by: Pablo * fix displayText Signed-off-by: Pablo * fix test Signed-off-by: Pablo --------- Signed-off-by: Pablo * [ANDROAPP-2474] Import/Export database (#3503) Signed-off-by: Pablo * fix(translations): sync translations from transifex (develop) * fix FormTest (#3523) * fix FormTest Signed-off-by: Pablo * remove commented code Signed-off-by: Pablo --------- Signed-off-by: Pablo * Set compose forms as default * verifying signed commit * fix: [ANDROAPP-5802] scheduling program stages (#3518) * fix: [ANDROAPP-5802] scheduling program stages * fix: [ANDROAPP-5802] scheduling program stages * fix black screen when qr is read (#3524) Signed-off-by: Pablo * Update SearchTETest (#3525) * Update SearchTETest with lazyActivityScenario and composeTestRule * fix shouldShowErrorWhenSyncEventFails * revert Jenkinsfile changes * shouldShowErrorWhenSyncEventFails click on event item * feat: [ANDROAPP-5930] move details information to the registration form (#3512) * feat: [ANDROAPP-5930] Initial commit: move enrollement date to formulary, check org unit open/close dates, modify enrollment screen * feat: [ANDROAPP-5930] update allowed dates based on selected Org Unit correctly * feat: [ANDROAPP-5930] remove unnecessary code and comments * feat: [ANDROAPP-5930] amend sonar fix for failed test * feat: [ANDROAPP-5930] correct flaky test and adapt to new enrollment flow * feat: [ANDROAPP-5930] readapt instrumentation tests to new enrollment flow * fix: [ANDROAPP-5930] sonar fix * fix: [ANDROAPP-5930] format style and add Unit tests for EnrollmentRepository class * fix: [ANDROAPP-5800] search page corrections (#3517) * fix: [ANDROAPP-5800] filter by equals instead of contains if parameter is option set, order search params * fix: [ANDROAPP-5800] correct value change when switching between age, month year inputs * fix: [ANDROAPP-5800] lint check * [ANDROAPP-5911] Fix paging3 mapping (#3526) Co-authored-by: Manu Muñoz Signed-off-by: Pablo * [ANDROAPP-5123] Support for custom icons (#3510) Co-authored-by: Manu Muñoz --------- Signed-off-by: Pablo * [ANDROAPP-5309] Line listing in local analytics (#3516) Signed-off-by: Pablo Co-authored-by: manu * fix: [ANDROAPP-5976] crash after discard or delete event (#3533) * feat: [ANDROAPP-5952] Update design system and adapt InputAge to new … (#3531) * feat: [ANDROAPP-5952] Update design system and adapt InputAge to new implementation, add tests for component * fix: [ANDROAPP-5952] take timezone into account for date setting * fix: [ANDROAPP-5952] code clean up * fix: [ANDROAPP-5952] update design system * fix: [ANDROAPP-5952] fix test * Androapp 5996 error in timeline view when there are no events created (#3527) * fix groupByStage value set method * fix timeline events recycler view when no events * fix the event count in timeline header * revert get grouping change --------- Co-authored-by: Siddharth Agarwal * fix: [ANDROAPP-5988] crash when returning to dashboard event list (#3540) * fix feedback on delete program enrollments (#3537) * fix feedback on delete program enrollments * fix lint error --------- Co-authored-by: Siddharth Agarwal * fix event card item margins (#3536) * fix event card item margins * fix lint error --------- Co-authored-by: Siddharth Agarwal * ANDROAPP-5799-search-enroll-button (#3535) * Load TE type name when search view model is created * Use new FAB component for `CreateNewButton` * Update search button UI * Add old search button composable * Remove `SearchTEListScreen.kt` * Add `Add new $teType` button * Rename `SearchButton_Old` to `SearchButtonWithQuery` * Show create TEI FAB if there is query data * Display old search button when there is search query data * Use `CoordinatorLayout` in `fragment_search_list` layout Rather than listening to scroll state changes manually, we can just leverage the inbuilt scroll behaviour of coordinator which is smoother. We are using a fake appbar to wrap the header content essentially. * Remove animated visibility from `FullSearchButtonAndWorkingList` * Use immutable map in `FullSearchButtonAndWorkingList` composable * Update design of search bar with query data * Show search bar with query data and create new entity button in landscape * Add icon to search button * Use lower case TE type name in the create button * Update working list chip group spacing * fix: [ANDROAPP-5982] "All [TE Type]" search and relationship search crash (#3542) * [ANDROAPP-5123] Do not use app MetadataIcon (#3539) * refactor custom icons Signed-off-by: Pablo * fix program metadata icon Signed-off-by: Pablo * fix tests Signed-off-by: Pablo * fix eventi Signed-off-by: Pablo --------- Signed-off-by: Pablo * [ANDROAPP-5309] fix table dimensions (#3541) * fix table dimensions Signed-off-by: Pablo * design comments Signed-off-by: Pablo --------- Signed-off-by: Pablo Co-authored-by: Andrés Miguel Rubio * fix: [ANDROAPP-5930] remove old unnecessary header and modify edit en… (#3538) * fix: [ANDROAPP-5930] remove old unnecessary header and modify edit enrollment corners and header * fix: [ANDROAPP-5930] wait for idle in test * fix: [ANDROAPP-5930] refactor methods * [ANDROAPP-5952] input age prompt stays at beginning (#3543) * refactor custom icons Signed-off-by: Pablo * fix: [ANDROAPP-5930] update design system and adapt input for textfieldValue * fix: [ANDROAPP-5930] add null checks for value format * fix: [ANDROAPP-5952] correct test * fix: [ANDROAPP-5952] amend rebase conflic resolution update design system --------- Signed-off-by: Pablo Co-authored-by: Pablo * [ANDROAPP-5805] Move details information to Event Data entry (#3528) * [ANDROAPP-5805] Simplify if else on EventDate * [ANDROAPP-5805] Remove MviIntent interface * [ANDROAPP-5805] Remove nullable parameters forced by using search * [ANDROAPP-5805] Add Event details section with report date * [ANDROAPP-5805] Add Event Org unit in details * [ANDROAPP-5805] Add Event Coordinates in details * [ANDROAPP-5805] Add Event Category Combo in details * [ANDROAPP-5805] Create Eventwithout registration and navigate to Dataentry * [ANDROAPP-5805] Add CreateEventUseCase * [ANDROAPP-5805] update saving attribute option combo * [ANDROAPP-5805] move PeriodUtils to common * [ANDROAPP-5805] Implement period selector for events * [ANDROAPP-5805] Set EventMode and allow org unit edition on creation * [ANDROAPP-5805] Add eventCatCombo parameter to equals method in FieldUiModelImpl * [ANDROAPP-5805] Remove Event Detail section * [ANDROAPP-5805] Remove date on CreateEventUseCase * [ANDROAPP-5805] load selected category option combo * [ANDROAPP-5805] fix test dependencies * [ANDROAPP-5805] fix ProgramEventTest * [ANDROAPP-5805] fix EventTest * [ANDROAPP-5805] ktlintFormat after rebasing custom icons * [ANDROAPP-5805] Add editable reason to EventCaptureForm * [ANDROAPP-5931] Collapse event details and cat combo sections if completed (#3530) Signed-off-by: Pablo * [ANDROAPP-5805] fix collapsible when no coordinates * [ANDROAPP-5805] Fix ProgramEventTest * [ANDROAPP-5805] Fix FormTest * [ANDROAPP-5805] Fix EventTest * [ANDROAPP-5805] Fix ProgramEventTest * [ANDROAPP-5805] Add idlingResource to EventCaptureFormPresenter * [ANDROAPP-5805] Handle remove org unit when creating event * [ANDROAPP-5805] Handle adding cat combo * [ANDROAPP-5805] Handle adding cat combo * [ANDROAPP-5805] fix code smells * [ANDROAPP-5805] fix code smells --------- Signed-off-by: Pablo Co-authored-by: Pablo * Change start padding of event item when grouped (#3546) * Update android sdk 1.10.0 snapshot 36 (#3547) * fix: [ANDROAPP-5923] All enrollment screen crash (#3534) * prevents crash on All enrollments screen * removes deleted view tags from test * removes useless non-null access * Add idilingResource to event tests (#3550) * Update release start job (#3549) * release_start workflow * Update release-start.yml --------- Co-authored-by: manu * Update version to 2.10 * fix: [ANDROAPP-6003] multiselection checked values (#3545) * fix: [ANDROAPP-5981] the app crashes randomly when scheduling new events in tracker program (#3521) * fix: [ANDROAPP-5981] filter by distinct events when updating to avoid duplicate calls to same event * fix: [ANDROAPP-5981] remove unnecessary subscription to observer, remove NonNullAsserted !! assertions for option codes and org unit codes * fix: [ANDROAPP-5981] code smells and small refactor * fix: [ANDROAPP-5981] update with requested changes * fix: [ANDROAPP-5981] ignore flaky test * [ANDROAPP-6010] defect event details catcombo scope fails when modifying org unit (#3555) * [ANDROAPP-6010] Adapt Event toolbar to new design * [ANDROAPP-6010] Set ProgramCaptureScope to event orgunit * [ANDROAPP-6010] check category combos for mandatory fields * fix: [ANDROAPP-6006] update changes on dashboard (#3554) * Add IdlingResource when completing an event (#3556) * Update android sdk 1.10.0 snapshot 37 (#3560) Co-authored-by: Andrés Miguel Rubio * fix icon size and alignment of program stage item (#3552) Co-authored-by: Siddharth Agarwal * fi: [ANDROAPP-6004] All {TE Type} search now show too many result. (#3553) * Update android sdk 1.10.0 snapshot 38 (#3562) * Update android sdk 1.10.0 snapshot 38 * Adapt unit tests * fix: [ANDROAPP-6017] only searched fields on searched bar (#3559) * update desyng system (#3544) Signed-off-by: Pablo * add anr reports in sentry (#3532) Signed-off-by: Pablo * [ANDROAPP-6007] Use default color in metadata icons (#3557) * use default color Signed-off-by: Pablo * fix unit tests Signed-off-by: Pablo * remove unused Signed-off-by: Pablo --------- Signed-off-by: Pablo * fix: [ANDROAPP-6024] table resize dimensions (#3566) * [ANDROAPP-6023] Scheduled event missbehavior (#3565) * [ANDROAPP-6023] Remove Event initial step when scheduling and event * [ANDROAPP-6023] add event form aesthetics rounded corners * [ANDROAPP-6023] set entollment from schedule and by generation after create an enrollment as new events. * [ANDROAPP-6023] Set EventMode Schedule when entering data scheduling an event * [ANDROAPP-6023] Remove unused EventCaptureInitial package * [ANDROAPP-6023] Change Enter/Cancel button text by Enter/Skip * [ANDROAPP-6023] Set rounded corners on EventCaptureActivity sections * fix: [ANDROAPP-6008] user-friendly name on search parameters (#3563) * updates Dashboard only when new tei dashboard card is shown (#3567) * fix: [ANDROAPP-6013] don't save events without date or catCombo (#3569) * fix: [ANDROAPP-6040] Remove scroll to first empty item functionality (#3571) * fix: [ANDROAPP-6040] Remove scroll to first empty item functionality * fix: [ANDROAPP-6040] adapt test now that automatic scroll does not occur * [Androapp-5763] cropped number when data element name wraps into second line (#3570) * feat: [ANDROAPP-5763] implement Indicator Input throughout app * [ANDROAPP-5763] Use biggerOrEquals for filtering legends Signed-off-by: andresmr * [ANDROAPP-5763] Set SurfaceColor.Container as default color Signed-off-by: andresmr * [ANDROAPP-5763] Remove xml from IndicatorViewHolder Signed-off-by: andresmr * [ANDROAPP-5763] Replace test robots Signed-off-by: andresmr * [ANDROAPP-5763] Remove uncommented test code Signed-off-by: andresmr * [ANDROAPP-5763] Move spacing to vertical arrangement Signed-off-by: andresmr --------- Signed-off-by: andresmr Co-authored-by: Xavier Molloy * fix: [ANDROAPP-6038] improved management for Cancel event in OU dialog (#3572) * fix: [ANDROAPP-6038] enable add new button when exiting canceling OU dialog * fix: [ANDROAPP-6038] small code refactor * Update design library to 0.2-20240405.110637-52 (#3575) Signed-off-by: andresmr * [ANDROAPP-6086] set today es event date when creating new event (#3577) Signed-off-by: andresmr * fix: [ANDROAPP-6033] modify padding between ListCard components, add superior padding if item is first one in order to improve superior border visibility (#3568) * fix: [ANDROAPP-6011] freeze getting dashboard events (#3574) * fix: [ANDROAPP-6011] freeze getting dashboard events * fix test * fix test * access localdatastore in background thread (#3580) Signed-off-by: Pablo * Create build-release-candidate.yml (#3573) * Create build-release-candidate.yml * Update build-release-candidate.yml * buid release candidate signing config Signed-off-by: Pablo * set keystore path Signed-off-by: Pablo * set missing env variables Signed-off-by: Pablo * ktlint Signed-off-by: Pablo * ignore schedule event test --------- Signed-off-by: Pablo Co-authored-by: manu * [ANDROAPP-6015] update design system (#3585) Signed-off-by: andresmr * fix: [ANDROAPP-6091] days to scheduled day (#3578) * update version name (#3584) * fix: [ANDROAPP-6053] ANR on dataset list (#3583) * [ANDROAPP-5712] Update sync dialog when reopened (#3564) * Update sync dialog when reopened Signed-off-by: Pablo * wait for compose to idle Signed-off-by: Pablo * use indeterminate progress in granular sync Signed-off-by: Pablo * wait for the data to be loaded Signed-off-by: Pablo --------- Signed-off-by: Pablo * Get period step on background (#3582) Signed-off-by: Pablo * fix: [ANDROAPP-6110] crash in program default metadata icon (#3594) * fix: Improve performance in home and event program screen (analytics tab) (#3595) * Improve performance in home screen (analytics tab) * Improve performance in program event screen (analytics tab) * Refactor condition * Restore deleted code * fix: [ANDROAPP-6103] month value in scheduling dialog (#3590) * fix component focus requester (#3587) Co-authored-by: Siddharth Agarwal * [ANDROAPP-6106] Replace enrollment label with custom one (#3592) * replace enrollment label with custom one Signed-off-by: Pablo * replace enrollment label with custom one in complete dialog Signed-off-by: Pablo * Restore enrollment list label Signed-off-by: Pablo --------- Signed-off-by: Pablo * Fix IndexOutOfBounds exception in SyncStat (#3597) Signed-off-by: Pablo * fix: [ANDROAPP-5992] check program stage filters in WorkingListScope (#3600) * fix: [ANDROAPP-5992] check program stage filters in WorkingListScope * fix: [ANDROAPP-5992] check program stage filters in WorkingListScope * [ANDROAPP-6012] Display validation errors (#3599) * [ANDROAPP-6012] Display validation errors Signed-off-by: Pablo * apply validation only for date value dates Signed-off-by: Pablo --------- Signed-off-by: Pablo * Skip OU selection for single option in event creation (#3588) * skip OU selection when count is 1 * fix lint error * Fix nullable programUid for OURepository * Fix TEIPresenter Test * Code refactored:move functions to repository class * Fix lint error --------- Co-authored-by: Siddharth Agarwal * fix: [ANDROAPP-6081] set current date for new enrollment (#3603) * [ANDROAPP-5949] OpenID login button overlaps with "Manage accounts" (#3606) Signed-off-by: Pablo * fix: [ANDROAPP-6119] verify array size before accessing index (#3602) Co-authored-by: Xavier Molloy * [ANDROAPP-5787] Use search scope in a referral (#3608) * [ANDROAPP-5787] Use search scope in a referral Signed-off-by: Pablo * [ANDROAPP-5787] add test Signed-off-by: Pablo * search scope must return search and capture org units Signed-off-by: Pablo * search scope must return search and capture org units Signed-off-by: Pablo * fix test Signed-off-by: Pablo --------- Signed-off-by: Pablo * browserstack build tag (#3610) * fix: [ANDROAPP-6123] key text in list cards (#3614) Co-authored-by: DavidAparicioAlbaAsenjo <137989685+DavidAparicioAlbaAsenjo@users.noreply.github.com> * [ANDROAPP-5869] Provide different keys for fixedStickyHeader (#3607) Signed-off-by: Pablo * fix: [ANDROAPP-5883] periods when orgunit is closed (#3613) * [ANDROAPP-5757] Field with error can't be disabled (#3609) Signed-off-by: Pablo * [ANDROAPP-6126] Date parse exception when searching by unfinished date string (#3604) Signed-off-by: Pablo * fix: [ANDROAPP-6062] clear search fields when required attributes to search (#3581) * fix: [ANDROAPP-6062] clear search fields when required attributes to search * add clear test --------- Signed-off-by: Pablo Signed-off-by: andresmr Co-authored-by: Siddharth Agarwal Co-authored-by: Siddharth Agarwal Co-authored-by: Andrés Miguel Rubio Co-authored-by: Xavier Molloy <44061143+xavimolloy@users.noreply.github.com> Co-authored-by: Sasikanth Miriyampalli Co-authored-by: Manu Muñoz Co-authored-by: Xavier Molloy Co-authored-by: FerdyRod Co-authored-by: = Co-authored-by: dhis2-bot Co-authored-by: manu Co-authored-by: FerdyRod Co-authored-by: Víctor García Co-authored-by: GitHub Actions Bot Co-authored-by: DavidAparicioAlbaAsenjo <137989685+DavidAparicioAlbaAsenjo@users.noreply.github.com> * fix landscape support Signed-off-by: andresmr --------- Signed-off-by: andresmr Signed-off-by: Pablo Co-authored-by: Pablo Co-authored-by: Siddharth Agarwal Co-authored-by: Siddharth Agarwal Co-authored-by: Xavier Molloy <44061143+xavimolloy@users.noreply.github.com> Co-authored-by: Sasikanth Miriyampalli Co-authored-by: Manu Muñoz Co-authored-by: Xavier Molloy Co-authored-by: FerdyRod Co-authored-by: = Co-authored-by: dhis2-bot Co-authored-by: manu Co-authored-by: FerdyRod Co-authored-by: Víctor García Co-authored-by: GitHub Actions Bot Co-authored-by: DavidAparicioAlbaAsenjo <137989685+DavidAparicioAlbaAsenjo@users.noreply.github.com> --- .github/workflows/build-release-candidate.yml | 65 ++++++ .github/workflows/publish-libraries.yml | 32 +++ Jenkinsfile | 3 + app/build.gradle.kts | 14 +- .../usescases/event/EventRegistrationRobot.kt | 2 + .../org/dhis2/usescases/filters/FilterTest.kt | 2 +- .../usescases/flow/syncFlow/SyncFlowTest.kt | 4 + .../org/dhis2/usescases/form/FormRobot.kt | 17 +- .../java/org/dhis2/usescases/form/FormTest.kt | 28 +-- .../programevent/ProgramEventTest.kt | 2 + .../programevent/robot/ProgramEventsRobot.kt | 2 - .../teidashboard/TeiDashboardTest.kt | 5 +- .../teidashboard/robot/IndicatorsRobot.kt | 64 ++---- app/src/main/java/org/dhis2/App.java | 1 + .../bindings/ValidationStrategyExtensions.kt | 9 - .../dhis2/data/dhislogic/DhisEventUtils.kt | 20 -- .../dhis2/data/forms/dataentry/ValueStore.kt | 5 - .../data/forms/dataentry/ValueStoreImpl.kt | 63 ------ .../dhis2/data/service/SyncGranularWorker.kt | 62 +++++- .../dhis2/data/service/SyncPresenterImpl.kt | 2 +- .../dataSetDetail/DataSetDetailFragment.kt | 3 +- .../datasetList/DataSetListAdapter.kt | 38 +++- .../datasetList/DataSetListViewModel.kt | 5 +- .../enrollment/EnrollmentActivity.kt | 27 +-- .../enrollment/EnrollmentPresenterImpl.kt | 29 +-- .../usescases/enrollment/EnrollmentView.kt | 1 - .../events/ScheduledEventActivity.kt | 21 +- .../events/ScheduledEventContract.kt | 1 - .../usescases/events/ScheduledEventModule.kt | 4 +- .../events/ScheduledEventPresenterImpl.kt | 10 +- .../eventCapture/EventCaptureActivity.kt | 67 +++--- .../eventCapture/EventCaptureContract.java | 166 --------------- .../eventCapture/EventCaptureContract.kt | 93 +++++++++ .../eventCapture/EventCapturePresenterImpl.kt | 56 +++-- .../EventCaptureRepositoryImpl.java | 33 +-- .../domain/ConfigureEventCompletionDialog.kt | 2 +- .../EventCaptureFormFragment.java | 2 +- .../EventCaptureFormPresenter.kt | 8 +- .../EventCaptureInitialComponent.kt | 8 - .../EventCaptureInitialFragment.kt | 5 - .../EventCaptureInitialModule.kt | 6 - .../model/EventCaptureInitialInfo.kt | 2 - .../data/EventDetailsRepository.kt | 27 ++- .../domain/ConfigureEventDetails.kt | 10 +- .../injection/EventDetailsModule.kt | 1 + .../eventDetails/ui/EventDetailsFragment.kt | 26 ++- .../EventInitialRepositoryImpl.java | 13 +- .../usescases/main/HomeRepositoryImpl.kt | 2 +- .../org/dhis2/usescases/main/MainPresenter.kt | 6 +- .../main/program/ProgramRepositoryImpl.kt | 5 +- .../ProgramEventDetailActivity.kt | 9 +- .../ProgramEventDetailLiveAdapter.kt | 11 +- .../ProgramEventDetailModule.kt | 7 + .../ProgramEventDetailRepositoryImpl.kt | 2 +- .../programEventDetail/ProgramEventMapper.kt | 4 + .../usecase/CreateEventUseCase.kt | 5 + .../ProgramStageSelectionInjector.kt | 8 +- .../ProgramStageSelectionPresenter.kt | 3 +- .../SearchRepositoryImpl.java | 14 +- .../SearchRepositoryImplKt.kt | 13 +- .../searchTrackEntity/SearchTEIViewModel.kt | 109 ++++++++-- .../adapters/BaseTeiViewHolder.kt | 3 +- .../adapters/SearchTeiLiveAdapter.kt | 39 +++- .../listView/SearchResult.kt | 2 +- .../listView/SearchResultHolder.kt | 8 +- .../listView/SearchTEList.kt | 25 ++- .../SearchParametersScreen.kt | 1 + .../model/SearchParametersUiState.kt | 1 + .../searchTrackEntity/ui/SearchTEUi.kt | 2 +- .../ui/mapper/TEICardMapper.kt | 9 +- .../teiDashboard/DashboardRepositoryImpl.kt | 5 +- .../teiDashboard/DashboardViewModel.kt | 29 +-- .../indicators/BaseIndicatorRepository.kt | 70 ++++--- .../RelationshipRepositoryImpl.kt | 7 +- .../teidata/TEIDataContracts.kt | 5 - .../teidata/TEIDataFragment.kt | 45 ++--- .../teidata/TEIDataModule.kt | 6 + .../teidata/TEIDataPresenter.kt | 98 +++------ .../teidata/TeiDataRepository.kt | 2 + .../teidata/TeiDataRepositoryImpl.kt | 64 +++++- .../teidata/teievents/EventAdapter.kt | 8 +- .../teidata/teievents/StageViewHolder.kt | 3 +- .../dialogs/scheduling/SchedulingViewModel.kt | 3 +- .../TeiProgramListRepositoryImpl.java | 1 + .../TroubleshootingRepository.kt | 3 +- .../customviews/PeriodDialogInputPeriod.java | 4 +- .../granularsync/GranularSyncPresenter.kt | 34 +++- .../utils/granularsync/SyncStatusDialog.kt | 26 ++- .../res/layout/activity_event_capture.xml | 29 +-- app/src/main/res/layout/activity_login.xml | 3 +- .../main/res/layout/fragment_indicators.xml | 4 +- app/src/main/res/layout/fragment_notes.xml | 6 +- .../res/layout/fragment_relationships.xml | 6 +- app/src/main/res/layout/item_dataset.xml | 5 +- .../res/layout/item_search_tracked_entity.xml | 5 +- .../res/layout/section_selector_fragment.xml | 9 +- app/src/main/res/values/strings.xml | 5 +- .../org/dhis2/bindings/DateExtensionsTest.kt | 73 ++++++- .../dhis2/bindings/TEICardExtensionsTest.kt | 10 +- .../dhis2/data/services/SyncPresenterTest.kt | 8 +- .../enrollment/EnrollmentPresenterImplTest.kt | 83 +++----- .../events/ScheduledEventPresenterImplTest.kt | 14 +- .../eventCapture/EventCapturePresenterTest.kt | 79 +------- .../EventCaptureRepositoryImplTest.kt | 88 -------- .../eventCapture/EventIntegrationTest.kt | 191 ++++++++++++++++-- .../EventCaptureFormPresenterTest.kt | 3 + .../data/EventDetailsRepositoryTest.kt | 60 ++++++ .../EventInitialRepositoryImplTest.kt | 23 ++- .../main/program/ProgramRepositoryImplTest.kt | 2 +- .../ProgramEventMapperTest.kt | 2 +- .../usecase/CreateEventUseCaseTest.kt | 11 + .../ProgramStageSelectionPresenterTest.kt | 2 +- .../SearchTEIViewModelTest.kt | 145 +++++++++++++ .../teiDashboard/DashboardViewModelTest.kt | 2 + .../data/TeiDataPresenterTest.kt | 91 ++++++--- .../scheduling/SchedulingViewModelTest.kt | 53 +++++ .../dhis2/utils/filters/FilterManagerTest.kt | 3 + .../dhis2/commons/bindings/FileExtensions.kt | 9 +- .../commons/bindings/TEICardExtensions.kt | 12 +- .../org/dhis2/commons/data/EventViewModel.kt | 2 + .../dhis2/commons/data/SearchTeiModel.java | 2 +- .../org/dhis2/commons/date/DateExtensions.kt | 16 +- .../org/dhis2/commons/date/DateUtils.java | 6 + .../filters/data/TeiWorkingListScope.kt | 6 + .../commons/orgunitselector/OUTreeFragment.kt | 15 +- .../commons/resources/MetadataIconProvider.kt | 10 +- .../org/dhis2/composetable/ui/ItemValues.kt | 10 +- .../composetable/ui/MultiOptionSelector.kt | 2 +- .../dhis2/composetable/ui/TableDimensions.kt | 25 ++- .../org/dhis2/composetable/ui/TableItemRow.kt | 6 +- .../ui/extensions/LazyListScopeExtensions.kt | 4 +- .../RuleEngineExtensions.kt | 4 +- .../mobileProgramRules/RulesRepository.kt | 20 +- .../dhis2/maps/carousel/CarouselTeiHolder.kt | 3 +- .../java/dhis2/org/analytics/charts/Charts.kt | 2 + .../dhis2/org/analytics/charts/data/Chart.kt | 6 +- .../org/analytics/charts/di/ChartsInjector.kt | 8 +- .../charts/mappers/GraphToIndicator.kt | 55 +++++ .../analytics/charts/mappers/GraphToValue.kt | 53 ----- .../providers/PeriodStepProviderImpl.kt | 75 +++++-- .../analytics/charts/ui/AnalyticsAdapter.kt | 7 +- .../charts/ui/IndicatorViewHolder.kt | 90 +++++++-- .../src/main/res/layout/item_indicator.xml | 82 -------- .../providers/PeriodStepProviderImplTest.kt | 25 ++- .../org/dhis2/form/bindings/RuleExtensions.kt | 4 +- .../org/dhis2/form/data/EventRepository.kt | 18 +- .../org/dhis2/form/data/FormRepositoryImpl.kt | 20 +- .../org/dhis2/form/data/FormValueStore.kt | 53 ++--- .../org/dhis2/form/data/RulesRepository.kt | 10 +- .../data/metadata/EnrollmentConfiguration.kt | 8 +- .../form/extensions/FieldUiModelExtensions.kt | 2 +- .../java/org/dhis2/form/model/EventMode.kt | 1 + form/src/main/java/org/dhis2/form/ui/Form.kt | 9 - .../provider/inputfield/CheckBoxProvider.kt | 9 - .../inputfield/CoordinatesProvider.kt | 4 - .../ui/provider/inputfield/FieldProvider.kt | 12 -- .../inputfield/MultiSelectionInputProvider.kt | 2 +- .../inputfield/RadioButtonProvider.kt | 5 - .../dhis2/form/data/FormRepositoryImplTest.kt | 36 ++++ .../org/dhis2/form/data/FormValueStoreTest.kt | 37 +++- gradle/libs.versions.toml | 8 +- scripts/browserstackJenkins.sh | 5 +- scripts/browserstackJenkinsCompose.sh | 3 +- scripts/browserstackJenkinsForm.sh | 3 +- .../rules/RuleValidationHelperImpl.kt | 2 +- .../android/rtsm/utils/RuleExtensions.kt | 4 +- .../main/java/org/dhis2/ui/MetadataIcon.kt | 8 +- .../java/org/dhis2/ui/MetadataIconData.kt | 2 +- .../bottomsheet/BottomSheetDialogContent.kt | 2 +- .../dialogs/bottomsheet/DialogButtonStyle.kt | 2 +- 170 files changed, 2128 insertions(+), 1478 deletions(-) create mode 100644 .github/workflows/build-release-candidate.yml create mode 100644 .github/workflows/publish-libraries.yml delete mode 100644 app/src/main/java/org/dhis2/bindings/ValidationStrategyExtensions.kt delete mode 100644 app/src/main/java/org/dhis2/data/dhislogic/DhisEventUtils.kt delete mode 100644 app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/EventCaptureContract.java create mode 100644 app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/EventCaptureContract.kt delete mode 100644 app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/eventInitialFragment/EventCaptureInitialComponent.kt delete mode 100644 app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/eventInitialFragment/EventCaptureInitialFragment.kt delete mode 100644 app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/eventInitialFragment/EventCaptureInitialModule.kt create mode 100644 app/src/test/java/org/dhis2/usescases/teiDashboard/dialogs/scheduling/SchedulingViewModelTest.kt create mode 100644 dhis_android_analytics/src/main/java/dhis2/org/analytics/charts/mappers/GraphToIndicator.kt delete mode 100644 dhis_android_analytics/src/main/java/dhis2/org/analytics/charts/mappers/GraphToValue.kt delete mode 100644 dhis_android_analytics/src/main/res/layout/item_indicator.xml diff --git a/.github/workflows/build-release-candidate.yml b/.github/workflows/build-release-candidate.yml new file mode 100644 index 0000000000..df7609ec4a --- /dev/null +++ b/.github/workflows/build-release-candidate.yml @@ -0,0 +1,65 @@ +# This is a basic workflow that is manually triggered + +name: Build Release Candidate + +env: + # The name of the main module repository + main_project_module: app + +# Controls when the action will run. Workflow runs when manually triggered using the UI +# or API. +on: + workflow_dispatch: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "greet" + greet: + # The type of runner that the job will run on + runs-on: ubuntu-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Runs a single command using the runners shell + - uses: actions/checkout@v3 + + # Set Repository Name As Env Variable + - name: Set repository name as env variable + run: echo "repository_name=$(echo '${{ github.repository }}' | awk -F '/' '{print $2}')" >> $GITHUB_ENV + + - name: Set Up JDK + uses: actions/setup-java@v3 + with: + distribution: 'zulu' # See 'Supported distributions' for available options + java-version: '17' + cache: 'gradle' + + - name: Change wrapper permissions + run: chmod +x ./gradlew + + - name: Decode Keystore + id: decode_keystore + uses: timheuer/base64-to-file@v1 + with: + fileName: 'dhis_keystore.jks' + encodedString: ${{ secrets.KEYSTORE }} + - name: build prod + run: ./gradlew app:assembleDhisRelease + env: + SENTRY_DSN: ${{ secrets.SENTRY_DSN }} + SIGNING_KEY_ALIAS: ${{ secrets.KEY_ALIAS }} + SIGNING_KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} + SIGNING_STORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }} + SIGNING_KEYSTORE_PATH: ${{ steps.decode_keystore.outputs.filePath }} + + - name: Read version name from file + working-directory: ./gradle + id: read-version + run: echo "::set-output name=vName::$(grep 'vName' libs.versions.toml | awk -F' = ' '{print $2}' | tr -d '"')" + + # Upload Artifact Build + - name: Upload Android artifacts + uses: actions/upload-artifact@v3 + with: + name: ${{ env.repository_name }} - Android APK + path: ${{ env.main_project_module }}/build/outputs/apk/dhis/release/dhis2-v${{ steps.read-version.outputs.vName }}-dhis-release.apk diff --git a/.github/workflows/publish-libraries.yml b/.github/workflows/publish-libraries.yml new file mode 100644 index 0000000000..94b5ccb317 --- /dev/null +++ b/.github/workflows/publish-libraries.yml @@ -0,0 +1,32 @@ +# This is a basic workflow that is manually triggered + +name: Publish libraries + +# Controls when the action will run. Workflow runs when manually triggered using the UI +# or API. +on: + workflow_dispatch: + # Inputs the workflow accepts. + inputs: + name: + # Friendly description to be shown in the UI instead of 'name' + description: 'Person to greet' + # Default value if no value is explicitly provided + default: 'World' + # Input has to be provided for the workflow to run + required: true + # The data type of the input + type: string + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "greet" + greet: + # The type of runner that the job will run on + runs-on: ubuntu-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Runs a single command using the runners shell + - name: Send greeting + run: echo "Hello ${{ inputs.name }}" diff --git a/Jenkinsfile b/Jenkinsfile index c9ac08f86d..5c8ba67d08 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -55,6 +55,7 @@ pipeline { BROWSERSTACK = credentials('android-browserstack') form_apk = sh(returnStdout: true, script: 'find form/build/outputs -iname "*.apk" | sed -n 1p') form_apk_path = "${env.WORKSPACE}/${form_apk}" + buildTag = "${env.GIT_BRANCH} - form" } steps { dir("${env.WORKSPACE}/scripts"){ @@ -71,6 +72,7 @@ pipeline { BROWSERSTACK = credentials('android-browserstack') compose_table_apk = sh(returnStdout: true, script: 'find compose-table/build/outputs -iname "*.apk" | sed -n 1p') compose_table_apk_path = "${env.WORKSPACE}/${compose_table_apk}" + buildTag = "${env.GIT_BRANCH} - table" } steps { dir("${env.WORKSPACE}/scripts"){ @@ -89,6 +91,7 @@ pipeline { test_apk = sh(returnStdout: true, script: 'find app/build/outputs -iname "*.apk" | sed -n 2p') app_apk_path = "${env.WORKSPACE}/${app_apk}" test_apk_path = "${env.WORKSPACE}/${test_apk}" + buildTag = "${env.GIT_BRANCH}" } steps { dir("${env.WORKSPACE}/scripts"){ diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 80f3d81242..535bb1ac95 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -35,6 +35,17 @@ android { } } + signingConfigs { + create("release"){ + keyAlias = System.getenv("SIGNING_KEY_ALIAS") + keyPassword = System.getenv("SIGNING_KEY_PASSWORD") + System.getenv("SIGNING_KEYSTORE_PATH")?.let {path-> + storeFile = file(path) + } + storePassword = System.getenv("SIGNING_STORE_PASSWORD") + } + } + testOptions { execution = "ANDROIDX_TEST_ORCHESTRATOR" unitTests { @@ -143,6 +154,7 @@ android { getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" ) + signingConfig = signingConfigs.getByName("release") buildConfigField("int", "MATOMO_ID", "1") buildConfigField("String", "BUILD_DATE", "\"" + getBuildDate() + "\"") buildConfigField("String", "GIT_SHA", "\"" + getCommitHash() + "\"") @@ -307,4 +319,4 @@ dependencies { androidTestImplementation(libs.test.compose.ui.test) androidTestImplementation(libs.test.hamcrest) androidTestImplementation(libs.dispatcher.dispatchEspresso) -} +} \ No newline at end of file diff --git a/app/src/androidTest/java/org/dhis2/usescases/event/EventRegistrationRobot.kt b/app/src/androidTest/java/org/dhis2/usescases/event/EventRegistrationRobot.kt index 1d91242e67..c13f41c502 100644 --- a/app/src/androidTest/java/org/dhis2/usescases/event/EventRegistrationRobot.kt +++ b/app/src/androidTest/java/org/dhis2/usescases/event/EventRegistrationRobot.kt @@ -3,6 +3,7 @@ package org.dhis2.usescases.event import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.junit4.ComposeTestRule import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performScrollTo import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.assertion.ViewAssertions.matches @@ -30,6 +31,7 @@ class EventRegistrationRobot : BaseRobot() { fun checkEventDataEntryIsOpened(completion: Int, email: String, composeTestRule: ComposeTestRule) { onView(withId(R.id.completion)).check(matches(hasCompletedPercentage(completion))) + composeTestRule.onNodeWithText(email).performScrollTo() composeTestRule.onNodeWithText(email).assertIsDisplayed() } diff --git a/app/src/androidTest/java/org/dhis2/usescases/filters/FilterTest.kt b/app/src/androidTest/java/org/dhis2/usescases/filters/FilterTest.kt index 28e714eab8..966881f270 100644 --- a/app/src/androidTest/java/org/dhis2/usescases/filters/FilterTest.kt +++ b/app/src/androidTest/java/org/dhis2/usescases/filters/FilterTest.kt @@ -146,7 +146,7 @@ class FilterTest : BaseTest() { eventWithoutRegistrationRobot(composeTestRule) { clickOnEventAtPosition(0) } - formRobot { + formRobot(composeTestRule) { clickOnSelectOption(1, 1) pressBack() pressBack() diff --git a/app/src/androidTest/java/org/dhis2/usescases/flow/syncFlow/SyncFlowTest.kt b/app/src/androidTest/java/org/dhis2/usescases/flow/syncFlow/SyncFlowTest.kt index 9d0f2001df..458845c14f 100644 --- a/app/src/androidTest/java/org/dhis2/usescases/flow/syncFlow/SyncFlowTest.kt +++ b/app/src/androidTest/java/org/dhis2/usescases/flow/syncFlow/SyncFlowTest.kt @@ -144,6 +144,7 @@ class SyncFlowTest : BaseTest() { prepareFacilityDataSetIntentAndLaunchActivity(ruleDataSet) dataSetRobot { + composeTestRule.waitForIdle() clickOnDataSetAtPosition(0) } @@ -166,7 +167,10 @@ class SyncFlowTest : BaseTest() { clickOnDataSetToSync(0) clickOnSyncButton() workInfoStatusLiveData.postValue(arrayListOf(mockedGranularWorkInfo(WorkInfo.State.RUNNING))) + composeTestRule.waitForIdle() workInfoStatusLiveData.postValue(arrayListOf(mockedGranularWorkInfo(WorkInfo.State.SUCCEEDED))) + composeTestRule.waitForIdle() + waitToDebounce(3000) checkSyncWasSuccessfully() //sync failed } cleanLocalDatabase() diff --git a/app/src/androidTest/java/org/dhis2/usescases/form/FormRobot.kt b/app/src/androidTest/java/org/dhis2/usescases/form/FormRobot.kt index 35e309b349..e42dfdab16 100644 --- a/app/src/androidTest/java/org/dhis2/usescases/form/FormRobot.kt +++ b/app/src/androidTest/java/org/dhis2/usescases/form/FormRobot.kt @@ -2,9 +2,11 @@ package org.dhis2.usescases.form import android.app.Activity import android.view.MenuItem +import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.junit4.ComposeTestRule import androidx.compose.ui.test.onAllNodesWithTag import androidx.compose.ui.test.onFirst +import androidx.compose.ui.test.onNodeWithText import androidx.test.espresso.Espresso.onData import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.click @@ -32,13 +34,16 @@ import org.hamcrest.CoreMatchers.`is` import org.hamcrest.CoreMatchers.not -fun formRobot(formRobot: FormRobot.() -> Unit) { - FormRobot().apply { +fun formRobot( + composeTestRule: ComposeTestRule, + formRobot: FormRobot.() -> Unit +) { + FormRobot(composeTestRule).apply { formRobot() } } -class FormRobot : BaseRobot() { +class FormRobot(val composeTestRule: ComposeTestRule) : BaseRobot() { fun clickOnASpecificSection(sectionLabel: String) { onView(withText(sectionLabel)).perform(click()) @@ -104,10 +109,8 @@ class FormRobot : BaseRobot() { } fun checkIndicatorIsDisplayed(name: String, value: String) { - onView(withId(R.id.indicator_name)) - .check(matches(allOf(isDisplayed(), withText(name)))) - onView(withId(R.id.indicator_value)) - .check(matches(allOf(isDisplayed(), withText(value)))) + composeTestRule.onNodeWithText(name).assertIsDisplayed() + composeTestRule.onNodeWithText(value).assertIsDisplayed() } fun checkLabel(label: String, position: Int) { diff --git a/app/src/androidTest/java/org/dhis2/usescases/form/FormTest.kt b/app/src/androidTest/java/org/dhis2/usescases/form/FormTest.kt index a02c59542e..06c74900a8 100644 --- a/app/src/androidTest/java/org/dhis2/usescases/form/FormTest.kt +++ b/app/src/androidTest/java/org/dhis2/usescases/form/FormTest.kt @@ -32,7 +32,7 @@ class FormTest : BaseTest() { fun shouldApplyProgramRules() { prepareIntentAndLaunchEventActivity(ruleEvent) - formRobot { + formRobot(composeTestRule) { clickOnASpecificSection("Gamma Rules A") } applyHideField() @@ -51,7 +51,7 @@ class FormTest : BaseTest() { } private fun applyHideField() { - formRobot { + formRobot(composeTestRule) { clickOnSelectOption( firstSectionPosition, HIDE_FIELD_POSITION @@ -61,7 +61,7 @@ class FormTest : BaseTest() { } private fun applyHideSection() { - formRobot { + formRobot(composeTestRule) { resetToNoAction(firstSectionPosition) clickOnSelectOption( firstSectionPosition, @@ -72,7 +72,7 @@ class FormTest : BaseTest() { } private fun applyShowWarning() { - formRobot { + formRobot(composeTestRule) { resetToNoAction(firstSectionPosition) clickOnSelectOption( firstSectionPosition, @@ -83,7 +83,7 @@ class FormTest : BaseTest() { } private fun applyShowError() { - formRobot { + formRobot(composeTestRule) { resetToNoAction(firstSectionPosition) clickOnSelectOption( firstSectionPosition, @@ -94,7 +94,7 @@ class FormTest : BaseTest() { } private fun applySetMandatoryField() { - formRobot { + formRobot(composeTestRule) { val nonMandatoryLabel = "ZZ TEST NUMBER" val mandatoryLabel = "ZZ TEST NUMBER *" val position = 5 @@ -109,7 +109,7 @@ class FormTest : BaseTest() { } private fun applyHideOption() { - formRobot { + formRobot(composeTestRule) { resetToNoAction(firstSectionPosition) clickOnSelectOption( firstSectionPosition, @@ -120,7 +120,7 @@ class FormTest : BaseTest() { } private fun applyHideOptionGroup() { - formRobot { + formRobot(composeTestRule) { resetToNoAction(firstSectionPosition) clickOnSelectOption( firstSectionPosition, @@ -132,7 +132,7 @@ class FormTest : BaseTest() { } private fun applyShowOptionGroup() { - formRobot { + formRobot(composeTestRule) { resetToNoAction(firstSectionPosition) clickOnSelectOption( firstSectionPosition, @@ -146,7 +146,7 @@ class FormTest : BaseTest() { } private fun applyAssignValue() { - formRobot { + formRobot(composeTestRule) { resetToNoAction(firstSectionPosition) clickOnSelectOption( firstSectionPosition, @@ -157,7 +157,7 @@ class FormTest : BaseTest() { } private fun applyDisplayText() { - formRobot { + formRobot(composeTestRule) { resetToNoAction(firstSectionPosition) clickOnSelectOption( firstSectionPosition, @@ -171,7 +171,7 @@ class FormTest : BaseTest() { } private fun applyDisplayKeyValue() { - formRobot { + formRobot(composeTestRule) { resetToNoAction(firstSectionPosition) clickOnSelectOption( firstSectionPosition, @@ -185,7 +185,7 @@ class FormTest : BaseTest() { } private fun applyWarningOnComplete() { - formRobot { + formRobot(composeTestRule) { resetToNoAction(firstSectionPosition) clickOnSelectOption( firstSectionPosition, @@ -200,7 +200,7 @@ class FormTest : BaseTest() { } private fun applyErrorOnComplete() { - formRobot { + formRobot(composeTestRule) { resetToNoAction(firstSectionPosition) clickOnSelectOption( firstSectionPosition, diff --git a/app/src/androidTest/java/org/dhis2/usescases/programevent/ProgramEventTest.kt b/app/src/androidTest/java/org/dhis2/usescases/programevent/ProgramEventTest.kt index b8894c8c08..68549602fa 100644 --- a/app/src/androidTest/java/org/dhis2/usescases/programevent/ProgramEventTest.kt +++ b/app/src/androidTest/java/org/dhis2/usescases/programevent/ProgramEventTest.kt @@ -12,6 +12,7 @@ import org.dhis2.usescases.programEventDetail.ProgramEventDetailActivity import org.dhis2.usescases.programevent.robot.programEventsRobot import org.dhis2.usescases.teidashboard.robot.eventRobot import org.junit.Before +import org.junit.Ignore import org.junit.Rule import org.junit.Test @@ -76,6 +77,7 @@ class ProgramEventTest : BaseTest() { } } + @Ignore("Flaky test, will be look om issue ANDROAPP-6030") @Test fun shouldCompleteAnEventAndReopenIt() { val eventDate = "15/3/2020" diff --git a/app/src/androidTest/java/org/dhis2/usescases/programevent/robot/ProgramEventsRobot.kt b/app/src/androidTest/java/org/dhis2/usescases/programevent/robot/ProgramEventsRobot.kt index 04cb72efce..d430956e6d 100644 --- a/app/src/androidTest/java/org/dhis2/usescases/programevent/robot/ProgramEventsRobot.kt +++ b/app/src/androidTest/java/org/dhis2/usescases/programevent/robot/ProgramEventsRobot.kt @@ -62,9 +62,7 @@ class ProgramEventsRobot(val composeTestRule: ComposeContentTestRule) : BaseRobo ).check(matches(isDisplayed())) } - @OptIn(ExperimentalTestApi::class) fun checkEventIsComplete(eventDate: String) { - composeTestRule.waitUntilAtLeastOneExists(hasText(eventDate)) composeTestRule.onNodeWithText(eventDate).assertIsDisplayed() composeTestRule.onNodeWithText("Event completed").assertIsDisplayed() } diff --git a/app/src/androidTest/java/org/dhis2/usescases/teidashboard/TeiDashboardTest.kt b/app/src/androidTest/java/org/dhis2/usescases/teidashboard/TeiDashboardTest.kt index 8ed623b3e9..4557f174bf 100644 --- a/app/src/androidTest/java/org/dhis2/usescases/teidashboard/TeiDashboardTest.kt +++ b/app/src/androidTest/java/org/dhis2/usescases/teidashboard/TeiDashboardTest.kt @@ -152,6 +152,7 @@ class TeiDashboardTest : BaseTest() { } } + @Ignore("To fix in ANDROAPP-6109") @Test fun shouldSuccessfullyScheduleAnEvent() { prepareTeiOpenedWithNoPreviousEventProgrammeAndLaunchActivity(rule) @@ -219,7 +220,7 @@ class TeiDashboardTest : BaseTest() { goToAnalytics() } - indicatorsRobot { + indicatorsRobot(composeTestRule) { checkDetails("0", "4817") } } @@ -302,7 +303,7 @@ class TeiDashboardTest : BaseTest() { goToAnalytics() } - indicatorsRobot { + indicatorsRobot(composeTestRule) { checkGraphIsRendered(chartName) } diff --git a/app/src/androidTest/java/org/dhis2/usescases/teidashboard/robot/IndicatorsRobot.kt b/app/src/androidTest/java/org/dhis2/usescases/teidashboard/robot/IndicatorsRobot.kt index 62f4c50948..5a6af744fe 100644 --- a/app/src/androidTest/java/org/dhis2/usescases/teidashboard/robot/IndicatorsRobot.kt +++ b/app/src/androidTest/java/org/dhis2/usescases/teidashboard/robot/IndicatorsRobot.kt @@ -1,73 +1,41 @@ package org.dhis2.usescases.teidashboard.robot +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.junit4.ComposeTestRule +import androidx.compose.ui.test.onNodeWithText import androidx.test.espresso.Espresso.onView import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.matcher.ViewMatchers.hasDescendant -import androidx.test.espresso.matcher.ViewMatchers.hasSibling -import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText import org.dhis2.R import org.dhis2.common.BaseRobot import org.dhis2.common.matchers.RecyclerviewMatchers.Companion.atPosition -import org.dhis2.common.matchers.RecyclerviewMatchers.Companion.isNotEmpty -import org.hamcrest.CoreMatchers.allOf -fun indicatorsRobot(indicatorsRobot: IndicatorsRobot.() -> Unit) { - IndicatorsRobot().apply { +fun indicatorsRobot( + composeTestRule: ComposeTestRule, + indicatorsRobot: IndicatorsRobot.() -> Unit +) { + IndicatorsRobot(composeTestRule).apply { indicatorsRobot() } } -class IndicatorsRobot : BaseRobot() { +class IndicatorsRobot(val composeTestRule: ComposeTestRule) : BaseRobot() { fun checkDetails(yellowFeverIndicator: String, weightIndicator: String) { - onView(withId(R.id.indicators_recycler)).check( - matches( - allOf( - isDisplayed(), isNotEmpty(), - atPosition( - 2, - hasDescendant( - allOf( - withText(yellowFeverIndicator), - hasSibling( - allOf( - withId(R.id.indicator_name), - withText("Measles + Yellow fever doses") - ) - ) - ) - ) - ) - ) - ) - ) + composeTestRule.onNodeWithText(yellowFeverIndicator).assertIsDisplayed() + composeTestRule.onNodeWithText(weightIndicator).assertIsDisplayed() + } + fun checkGraphIsRendered(chartName: String) { onView(withId(R.id.indicators_recycler)).check( matches( - allOf( - isDisplayed(), isNotEmpty(), - atPosition( - 1, - hasDescendant( - allOf( - withText(weightIndicator), - hasSibling( - allOf( - withId(R.id.indicator_name), - withText("Average weight (g)") - ) - ) - ) - ) - ) + atPosition( + 1, + hasDescendant(withText(chartName)) ) ) ) } - - fun checkGraphIsRendered(chartName:String){ - onView(withId(R.id.indicators_recycler)).check(matches(atPosition(1, hasDescendant(withText(chartName))))) - } } diff --git a/app/src/main/java/org/dhis2/App.java b/app/src/main/java/org/dhis2/App.java index a1efb3fc2f..099a38e85d 100644 --- a/app/src/main/java/org/dhis2/App.java +++ b/app/src/main/java/org/dhis2/App.java @@ -131,6 +131,7 @@ public void initCrashController() { if (areTrackingPermissionGranted()) { SentryAndroid.init(this, options -> { options.setDsn(BuildConfig.SENTRY_DSN); + options.setAnrReportInDebug(true); // Add a callback that will be used before the event is sent to Sentry. // With this callback, you can modify the event or, when returning null, also discard the event. diff --git a/app/src/main/java/org/dhis2/bindings/ValidationStrategyExtensions.kt b/app/src/main/java/org/dhis2/bindings/ValidationStrategyExtensions.kt deleted file mode 100644 index 63df251fb5..0000000000 --- a/app/src/main/java/org/dhis2/bindings/ValidationStrategyExtensions.kt +++ /dev/null @@ -1,9 +0,0 @@ -package org.dhis2.bindings - -import org.hisp.dhis.android.core.common.ValidationStrategy - -fun ValidationStrategy.canSkipErrorFix(hasErrorFields: Boolean, hasEmptyMandatoryFields: Boolean) = - when (this) { - ValidationStrategy.ON_COMPLETE -> true - ValidationStrategy.ON_UPDATE_AND_INSERT -> !hasErrorFields && !hasEmptyMandatoryFields - } diff --git a/app/src/main/java/org/dhis2/data/dhislogic/DhisEventUtils.kt b/app/src/main/java/org/dhis2/data/dhislogic/DhisEventUtils.kt deleted file mode 100644 index 27aac9e132..0000000000 --- a/app/src/main/java/org/dhis2/data/dhislogic/DhisEventUtils.kt +++ /dev/null @@ -1,20 +0,0 @@ -package org.dhis2.data.dhislogic - -import org.hisp.dhis.android.core.D2 -import org.hisp.dhis.android.core.common.FeatureType -import javax.inject.Inject - -class DhisEventUtils @Inject constructor(val d2: D2) { - fun newEventNeedsExtraInfo(eventUid: String): Boolean { - val event = d2.eventModule().events().uid(eventUid) - .blockingGet() - val stage = d2.programModule().programStages().uid(event?.programStage()) - .blockingGet() - val program = d2.programModule().programs().uid(stage?.program()?.uid()) - .blockingGet() - val hasCoordinates = stage?.featureType() != null && stage.featureType() != FeatureType.NONE - val hasNonDefaultCatCombo = d2.categoryModule().categoryCombos() - .uid(program?.categoryComboUid()).blockingGet()?.isDefault != true - return hasCoordinates || hasNonDefaultCatCombo - } -} diff --git a/app/src/main/java/org/dhis2/data/forms/dataentry/ValueStore.kt b/app/src/main/java/org/dhis2/data/forms/dataentry/ValueStore.kt index 0ba894599e..4381d11c82 100644 --- a/app/src/main/java/org/dhis2/data/forms/dataentry/ValueStore.kt +++ b/app/src/main/java/org/dhis2/data/forms/dataentry/ValueStore.kt @@ -18,11 +18,6 @@ interface ValueStore { fun deleteOptionValues(optionCodeValuesToDelete: List) fun deleteOptionValueIfSelected(field: String, optionUid: String): StoreResult - fun deleteOptionValueIfSelectedInGroup( - field: String, - optionGroupUid: String, - isInGroup: Boolean, - ): StoreResult fun overrideProgram(programUid: String?) fun validate(dataElementUid: String, value: String?): ValidatorResult diff --git a/app/src/main/java/org/dhis2/data/forms/dataentry/ValueStoreImpl.kt b/app/src/main/java/org/dhis2/data/forms/dataentry/ValueStoreImpl.kt index ebd4b20bd7..8ec8091e42 100644 --- a/app/src/main/java/org/dhis2/data/forms/dataentry/ValueStoreImpl.kt +++ b/app/src/main/java/org/dhis2/data/forms/dataentry/ValueStoreImpl.kt @@ -258,37 +258,6 @@ class ValueStoreImpl( } } - override fun deleteOptionValueIfSelectedInGroup( - field: String, - optionGroupUid: String, - isInGroup: Boolean, - ): StoreResult { - val optionsInGroup = - d2.optionModule().optionGroups() - .withOptions() - .uid(optionGroupUid) - .blockingGet() - ?.options() - ?.map { d2.optionModule().options().uid(it.uid()).blockingGet()?.code()!! } - ?: arrayListOf() - return when (entryMode) { - EntryMode.DE -> deleteDataElementValueIfNotInGroup( - field, - optionsInGroup, - isInGroup, - ) - EntryMode.ATTR -> deleteAttributeValueIfNotInGroup( - field, - optionsInGroup, - isInGroup, - ) - EntryMode.DV, - -> throw IllegalArgumentException( - "DataValues can't be saved using these arguments. Use the other one.", - ) - } - } - private fun deleteDataElementValue(field: String, optionUid: String): StoreResult { val option = d2.optionModule().options().uid(optionUid).blockingGet() val possibleValues = arrayListOf(option?.name(), option?.code()).filterNotNull() @@ -317,38 +286,6 @@ class ValueStoreImpl( } } - private fun deleteDataElementValueIfNotInGroup( - field: String, - optionCodesToShow: List, - isInGroup: Boolean, - ): StoreResult { - val valueRepository = - d2.trackedEntityModule().trackedEntityDataValues().value(recordUid, field) - return if (valueRepository.blockingExists() && - optionCodesToShow.contains(valueRepository.blockingGet()?.value()) == isInGroup - ) { - save(field, null).blockingFirst() - } else { - StoreResult(field, ValueStoreResult.VALUE_HAS_NOT_CHANGED) - } - } - - private fun deleteAttributeValueIfNotInGroup( - field: String, - optionCodesToShow: List, - isInGroup: Boolean, - ): StoreResult { - val valueRepository = - d2.trackedEntityModule().trackedEntityAttributeValues().value(field, recordUid) - return if (valueRepository.blockingExists() && - optionCodesToShow.contains(valueRepository.blockingGet()?.value()) == isInGroup - ) { - save(field, null).blockingFirst() - } else { - StoreResult(field, ValueStoreResult.VALUE_HAS_NOT_CHANGED) - } - } - override fun deleteOptionValues(optionCodeValuesToDelete: List) { when (entryMode) { EntryMode.DE -> deleteOptionValuesForEvents(optionCodeValuesToDelete) diff --git a/app/src/main/java/org/dhis2/data/service/SyncGranularWorker.kt b/app/src/main/java/org/dhis2/data/service/SyncGranularWorker.kt index 96122ae22f..02a769c62e 100644 --- a/app/src/main/java/org/dhis2/data/service/SyncGranularWorker.kt +++ b/app/src/main/java/org/dhis2/data/service/SyncGranularWorker.kt @@ -25,10 +25,18 @@ package org.dhis2.data.service +import android.app.NotificationChannel +import android.app.NotificationManager import android.content.Context +import android.content.pm.ServiceInfo +import android.os.Build +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import androidx.work.ForegroundInfo import androidx.work.Worker import androidx.work.WorkerParameters import org.dhis2.App +import org.dhis2.R import org.dhis2.commons.Constants.ATTRIBUTE_OPTION_COMBO import org.dhis2.commons.Constants.CATEGORY_OPTION_COMBO import org.dhis2.commons.Constants.CONFLICT_TYPE @@ -36,9 +44,10 @@ import org.dhis2.commons.Constants.ORG_UNIT import org.dhis2.commons.Constants.PERIOD_ID import org.dhis2.commons.Constants.UID import org.dhis2.commons.sync.ConflictType -import java.util.Objects import javax.inject.Inject +private const val GRANULAR_CHANNEL = "sync_granular_notification" +private const val SYNC_GRANULAR_ID = 8071988 class SyncGranularWorker( context: Context, workerParams: WorkerParameters, @@ -48,12 +57,18 @@ class SyncGranularWorker( internal lateinit var presenter: SyncPresenter override fun doWork(): Result { - Objects.requireNonNull((applicationContext as App).userComponent())!! - .plus(SyncGranularRxModule()).inject(this) + (applicationContext as App).userComponent() + ?.plus(SyncGranularRxModule())?.inject(this) val uid = inputData.getString(UID) ?: return Result.failure() val conflictType = inputData.getString(CONFLICT_TYPE)?.let { ConflictType.valueOf(it) } + triggerNotification( + title = applicationContext.getString(R.string.app_name), + content = applicationContext.getString(R.string.syncing_data), + progress = 0, + ) + val result = when (conflictType) { ConflictType.PROGRAM -> { presenter.blockSyncGranularProgram(uid) @@ -79,6 +94,47 @@ class SyncGranularWorker( ) else -> Result.failure() } + + cancelNotification() return result } + + private fun triggerNotification(title: String, content: String, progress: Int) { + val notificationManager = + applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val mChannel = NotificationChannel( + GRANULAR_CHANNEL, + "GranularSync", + NotificationManager.IMPORTANCE_HIGH, + ) + notificationManager.createNotificationChannel(mChannel) + } + val notificationBuilder: NotificationCompat.Builder = NotificationCompat.Builder( + applicationContext, + GRANULAR_CHANNEL, + ) + .setSmallIcon(R.drawable.ic_sync) + .setContentTitle(title) + .setContentText(content) + .setOngoing(true) + .setOnlyAlertOnce(true) + .setAutoCancel(false) + .setProgress(100, progress, true) + .setPriority(NotificationCompat.PRIORITY_DEFAULT) + setForegroundAsync( + ForegroundInfo( + SYNC_GRANULAR_ID, + notificationBuilder.build(), + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC else 0, + ), + ) + } + + private fun cancelNotification() { + val notificationManager = NotificationManagerCompat.from( + applicationContext, + ) + notificationManager.cancel(SYNC_GRANULAR_ID) + } } diff --git a/app/src/main/java/org/dhis2/data/service/SyncPresenterImpl.kt b/app/src/main/java/org/dhis2/data/service/SyncPresenterImpl.kt index 0da82acab8..9e1acd36e8 100644 --- a/app/src/main/java/org/dhis2/data/service/SyncPresenterImpl.kt +++ b/app/src/main/java/org/dhis2/data/service/SyncPresenterImpl.kt @@ -215,7 +215,7 @@ class SyncPresenterImpl( ).andThen( Completable.fromObservable( d2.fileResourceModule().fileResourceDownloader() - .byDomainType().eq(FileResourceDomainType.CUSTOM_ICON) + .byDomainType().eq(FileResourceDomainType.ICON) .download(), ), ).blockingAwait() diff --git a/app/src/main/java/org/dhis2/usescases/datasets/dataSetTable/dataSetDetail/DataSetDetailFragment.kt b/app/src/main/java/org/dhis2/usescases/datasets/dataSetTable/dataSetDetail/DataSetDetailFragment.kt index e570fbd008..cf0fa562b4 100644 --- a/app/src/main/java/org/dhis2/usescases/datasets/dataSetTable/dataSetDetail/DataSetDetailFragment.kt +++ b/app/src/main/java/org/dhis2/usescases/datasets/dataSetTable/dataSetDetail/DataSetDetailFragment.kt @@ -20,6 +20,7 @@ import org.hisp.dhis.android.core.common.ObjectStyle import org.hisp.dhis.android.core.dataset.DataSetInstance import org.hisp.dhis.android.core.period.Period import org.hisp.dhis.android.core.period.PeriodType +import org.hisp.dhis.mobile.ui.designsystem.theme.SurfaceColor import java.util.Date import java.util.Locale import javax.inject.Inject @@ -149,7 +150,7 @@ class DataSetDetailFragment private constructor() : FragmentGlobalAbstract(), Da override fun setStyle(style: ObjectStyle?) { style?.let { binding.composeDataSetIcon.setUpMetadataIcon( - metadataIconProvider(style), + metadataIconProvider(style, SurfaceColor.Primary), ) } } diff --git a/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/datasetList/DataSetListAdapter.kt b/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/datasetList/DataSetListAdapter.kt index d7ed9797e9..235945ccaf 100644 --- a/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/datasetList/DataSetListAdapter.kt +++ b/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/datasetList/DataSetListAdapter.kt @@ -3,6 +3,11 @@ package org.dhis2.usescases.datasets.datasetDetail.datasetList import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.ui.Modifier import androidx.compose.ui.platform.ComposeView import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter @@ -13,6 +18,7 @@ import org.dhis2.usescases.datasets.datasetDetail.DataSetDetailModel import org.dhis2.usescases.datasets.datasetDetail.datasetList.mapper.DatasetCardMapper import org.hisp.dhis.mobile.ui.designsystem.component.ListCard import org.hisp.dhis.mobile.ui.designsystem.component.ListCardTitleModel +import org.hisp.dhis.mobile.ui.designsystem.theme.Spacing class DataSetListAdapter( val viewModel: DataSetListViewModel, @@ -48,16 +54,28 @@ class DataSetListAdapter( viewModel.openDataSet(it) }, ) - ListCard( - listAvatar = card.avatar, - title = ListCardTitleModel(text = card.title), - lastUpdated = card.lastUpdated, - additionalInfoList = card.additionalInfo, - actionButton = card.actionButton, - expandLabelText = card.expandLabelText, - shrinkLabelText = card.shrinkLabelText, - onCardClick = card.onCardCLick, - ) + Column( + modifier = Modifier + .padding( + start = Spacing.Spacing8, + end = Spacing.Spacing8, + bottom = Spacing.Spacing4, + ), + ) { + if (position == 0) { + Spacer(modifier = Modifier.size(Spacing.Spacing8)) + } + ListCard( + listAvatar = card.avatar, + title = ListCardTitleModel(text = card.title), + lastUpdated = card.lastUpdated, + additionalInfoList = card.additionalInfo, + actionButton = card.actionButton, + expandLabelText = card.expandLabelText, + shrinkLabelText = card.shrinkLabelText, + onCardClick = card.onCardCLick, + ) + } } holder.bind(it, viewModel) diff --git a/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/datasetList/DataSetListViewModel.kt b/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/datasetList/DataSetListViewModel.kt index 3439625446..9eb943de26 100644 --- a/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/datasetList/DataSetListViewModel.kt +++ b/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/datasetList/DataSetListViewModel.kt @@ -23,7 +23,7 @@ class DataSetListViewModel( schedulerProvider: SchedulerProvider, val filterManager: FilterManager, val matomoAnalyticsController: MatomoAnalyticsController, - private val dispatcher: DispatcherProvider, + dispatcher: DispatcherProvider, ) : ViewModel() { @@ -45,13 +45,12 @@ class DataSetListViewModel( filterManager.asFlowable() .startWith(filterManager) .flatMap { filterManager: FilterManager -> - dataSetDetailRepository.dataSetGroups( filterManager.orgUnitUidsFilters, filterManager.periodFilters, filterManager.stateFilters, filterManager.catOptComboFilters, - ) + ).subscribeOn(schedulerProvider.io()) } .subscribeOn(schedulerProvider.io()) .observeOn(schedulerProvider.ui()) diff --git a/app/src/main/java/org/dhis2/usescases/enrollment/EnrollmentActivity.kt b/app/src/main/java/org/dhis2/usescases/enrollment/EnrollmentActivity.kt index b5952cf2a4..e9f318a053 100644 --- a/app/src/main/java/org/dhis2/usescases/enrollment/EnrollmentActivity.kt +++ b/app/src/main/java/org/dhis2/usescases/enrollment/EnrollmentActivity.kt @@ -10,7 +10,6 @@ import androidx.databinding.DataBindingUtil import com.google.android.material.dialog.MaterialAlertDialogBuilder import org.dhis2.App import org.dhis2.R -import org.dhis2.commons.Constants import org.dhis2.commons.Constants.ENROLLMENT_UID import org.dhis2.commons.Constants.PROGRAM_UID import org.dhis2.commons.Constants.TEI_UID @@ -32,7 +31,6 @@ import org.dhis2.ui.dialogs.bottomsheet.BottomSheetDialogUiModel import org.dhis2.ui.dialogs.bottomsheet.DialogButtonStyle import org.dhis2.usescases.events.ScheduledEventActivity import org.dhis2.usescases.eventsWithoutRegistration.eventCapture.EventCaptureActivity -import org.dhis2.usescases.eventsWithoutRegistration.eventInitial.EventInitialActivity import org.dhis2.usescases.general.ActivityGlobalAbstract import org.dhis2.usescases.teiDashboard.TeiDashboardMobileActivity import org.dhis2.utils.granularsync.OPEN_ERROR_LOCATION @@ -194,35 +192,15 @@ class EnrollmentActivity : ActivityGlobalAbstract(), EnrollmentView { if (presenter.isEventScheduleOrSkipped(eventUid)) { val scheduleEventIntent = ScheduledEventActivity.getIntent(this, eventUid) openEventForResult.launch(scheduleEventIntent) - } else if (presenter.openInitial(eventUid)) { - val bundle = EventInitialActivity.getBundle( - presenter.getProgram()?.uid(), - eventUid, - null, - presenter.getEnrollment()!!.trackedEntityInstance(), - null, - presenter.getEnrollment()!!.organisationUnit(), - presenter.getEventStage(eventUid), - presenter.getEnrollment()!!.uid(), - 0, - presenter.getEnrollment()!!.status(), - ) - val eventInitialIntent = Intent(abstracContext, EventInitialActivity::class.java) - eventInitialIntent.putExtras(bundle) - startActivityForResult(eventInitialIntent, RQ_EVENT) } else { val eventCreationIntent = Intent(abstracContext, EventCaptureActivity::class.java) eventCreationIntent.putExtras( EventCaptureActivity.getActivityBundle( eventUid, presenter.getProgram()?.uid() ?: "", - EventMode.CHECK, + EventMode.NEW, ), ) - eventCreationIntent.putExtra( - Constants.TRACKED_ENTITY_INSTANCE, - presenter.getEnrollment()!!.trackedEntityInstance(), - ) startActivityForResult(eventCreationIntent, RQ_EVENT) } } @@ -352,9 +330,6 @@ class EnrollmentActivity : ActivityGlobalAbstract(), EnrollmentView { binding.enrollmentStatus = status } - override fun showStatusOptions(currentStatus: EnrollmentStatus) { - } - /*endregion*/ override fun requestFocus() { diff --git a/app/src/main/java/org/dhis2/usescases/enrollment/EnrollmentPresenterImpl.kt b/app/src/main/java/org/dhis2/usescases/enrollment/EnrollmentPresenterImpl.kt index e0bfac9128..a87db3bff1 100644 --- a/app/src/main/java/org/dhis2/usescases/enrollment/EnrollmentPresenterImpl.kt +++ b/app/src/main/java/org/dhis2/usescases/enrollment/EnrollmentPresenterImpl.kt @@ -19,7 +19,6 @@ import org.dhis2.utils.analytics.AnalyticsHelper import org.dhis2.utils.analytics.DELETE_AND_BACK import org.hisp.dhis.android.core.D2 import org.hisp.dhis.android.core.arch.repositories.`object`.ReadOnlyOneObjectRepositoryFinalImpl -import org.hisp.dhis.android.core.common.FeatureType import org.hisp.dhis.android.core.common.Geometry import org.hisp.dhis.android.core.common.State import org.hisp.dhis.android.core.enrollment.Enrollment @@ -50,7 +49,6 @@ class EnrollmentPresenterImpl( private val eventCollectionRepository: EventCollectionRepository, private val teiAttributesProvider: TeiAttributesProvider, ) { - private var finishing: Boolean = false private val disposable = CompositeDisposable() private val backButtonProcessor: FlowableProcessor = PublishProcessor.create() private var hasShownIncidentDateEditionWarning = false @@ -168,6 +166,7 @@ class EnrollmentPresenterImpl( ), ) } + EnrollmentActivity.EnrollmentMode.CHECK -> view.setResultAndFinish() } } @@ -178,29 +177,12 @@ class EnrollmentPresenterImpl( view.showDateEditionWarning() } } - if (finishing) { - view.performSaveClick() - } - finishing = false } fun backIsClicked() { backButtonProcessor.onNext(true) } - fun openInitial(eventUid: String): Boolean { - val catComboUid = getProgram()?.categoryComboUid() - val event = d2.eventModule().events().uid(eventUid).blockingGet() - val stage = d2.programModule().programStages().uid(event?.programStage()).blockingGet() - val needsCatCombo = programRepository.blockingGet()?.categoryComboUid() != null && - d2.categoryModule().categoryCombos().uid(catComboUid) - .blockingGet()?.isDefault == false - val needsCoordinates = - stage?.featureType() != null && stage.featureType() != FeatureType.NONE - - return needsCatCombo || needsCoordinates - } - fun getEnrollment(): Enrollment? { return enrollmentObjectRepository.blockingGet() } @@ -224,8 +206,6 @@ class EnrollmentPresenterImpl( } } - fun hasAccess() = getProgram()?.access()?.data()?.write() ?: false - fun saveEnrollmentGeometry(geometry: Geometry?) { enrollmentObjectRepository.setGeometry(geometry) } @@ -258,13 +238,6 @@ class EnrollmentPresenterImpl( } } - fun setFinishing() { - finishing = true - } - - fun getEventStage(eventUid: String) = - enrollmentFormRepository.getProgramStageUidFromEvent(eventUid) - fun showOrHideSaveButton() { val teiUid = teiRepository.blockingGet()?.uid() ?: "" val programUid = getProgram()?.uid() ?: "" diff --git a/app/src/main/java/org/dhis2/usescases/enrollment/EnrollmentView.kt b/app/src/main/java/org/dhis2/usescases/enrollment/EnrollmentView.kt index bc0c10c7b0..9a953b3bd7 100644 --- a/app/src/main/java/org/dhis2/usescases/enrollment/EnrollmentView.kt +++ b/app/src/main/java/org/dhis2/usescases/enrollment/EnrollmentView.kt @@ -9,7 +9,6 @@ interface EnrollmentView : AbstractActivityContracts.View { fun setAccess(access: Boolean?) fun renderStatus(status: EnrollmentStatus) - fun showStatusOptions(currentStatus: EnrollmentStatus) fun setSaveButtonVisible(visible: Boolean) diff --git a/app/src/main/java/org/dhis2/usescases/events/ScheduledEventActivity.kt b/app/src/main/java/org/dhis2/usescases/events/ScheduledEventActivity.kt index 85f18d0d2c..28952bf12a 100644 --- a/app/src/main/java/org/dhis2/usescases/events/ScheduledEventActivity.kt +++ b/app/src/main/java/org/dhis2/usescases/events/ScheduledEventActivity.kt @@ -10,7 +10,6 @@ import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.databinding.DataBindingUtil import org.dhis2.App import org.dhis2.R -import org.dhis2.commons.data.EventCreationType import org.dhis2.commons.date.DateUtils import org.dhis2.commons.dialogs.PeriodDialog import org.dhis2.databinding.ActivityEventScheduledBinding @@ -21,7 +20,6 @@ import org.dhis2.usescases.eventsWithoutRegistration.eventDetails.models.EventIn import org.dhis2.usescases.eventsWithoutRegistration.eventDetails.providers.ProvideInputDate import org.dhis2.usescases.eventsWithoutRegistration.eventDetails.providers.ProvidePeriodSelector import org.dhis2.usescases.eventsWithoutRegistration.eventDetails.providers.willShowCalendar -import org.dhis2.usescases.eventsWithoutRegistration.eventInitial.EventInitialActivity import org.dhis2.usescases.general.ActivityGlobalAbstract import org.hisp.dhis.android.core.event.Event import org.hisp.dhis.android.core.event.EventStatus @@ -237,28 +235,11 @@ class ScheduledEventActivity : ActivityGlobalAbstract(), ScheduledEventContract. } } - override fun openInitialActivity() { - val bundle = EventInitialActivity.getBundle( - program.uid(), - event.uid(), - EventCreationType.DEFAULT.name, - presenter.getEventTei(), - stage.periodType(), - presenter.getEnrollment()?.organisationUnit(), - stage.uid(), - event.enrollment(), - stage.standardInterval() ?: 0, - presenter.getEnrollment()?.status(), - ) - startActivity(Intent(this, EventInitialActivity::class.java).apply { putExtras(bundle) }) - finish() - } - override fun openFormActivity() { val bundle = EventCaptureActivity.getActivityBundle( event.uid(), program.uid(), - EventMode.CHECK, + EventMode.SCHEDULE, ) Intent(activity, EventCaptureActivity::class.java).apply { putExtras(bundle) diff --git a/app/src/main/java/org/dhis2/usescases/events/ScheduledEventContract.kt b/app/src/main/java/org/dhis2/usescases/events/ScheduledEventContract.kt index 052a792c2a..dfc435ff34 100644 --- a/app/src/main/java/org/dhis2/usescases/events/ScheduledEventContract.kt +++ b/app/src/main/java/org/dhis2/usescases/events/ScheduledEventContract.kt @@ -16,7 +16,6 @@ class ScheduledEventContract { fun setEvent(event: Event) fun setStage(programStage: ProgramStage, event: Event) fun setProgram(program: Program) - fun openInitialActivity() fun openFormActivity() } diff --git a/app/src/main/java/org/dhis2/usescases/events/ScheduledEventModule.kt b/app/src/main/java/org/dhis2/usescases/events/ScheduledEventModule.kt index 8f54a87090..ff27377572 100644 --- a/app/src/main/java/org/dhis2/usescases/events/ScheduledEventModule.kt +++ b/app/src/main/java/org/dhis2/usescases/events/ScheduledEventModule.kt @@ -3,7 +3,6 @@ package org.dhis2.usescases.events import dagger.Module import dagger.Provides import org.dhis2.commons.di.dagger.PerActivity -import org.dhis2.data.dhislogic.DhisEventUtils import org.hisp.dhis.android.core.D2 @Module @@ -13,8 +12,7 @@ class ScheduledEventModule(val eventUid: String, val view: ScheduledEventContrac @PerActivity internal fun providePresenter( d2: D2, - eventUtils: DhisEventUtils, ): ScheduledEventContract.Presenter { - return ScheduledEventPresenterImpl(view, d2, eventUid, eventUtils) + return ScheduledEventPresenterImpl(view, d2, eventUid) } } diff --git a/app/src/main/java/org/dhis2/usescases/events/ScheduledEventPresenterImpl.kt b/app/src/main/java/org/dhis2/usescases/events/ScheduledEventPresenterImpl.kt index 0815cde501..fc8cebc39c 100644 --- a/app/src/main/java/org/dhis2/usescases/events/ScheduledEventPresenterImpl.kt +++ b/app/src/main/java/org/dhis2/usescases/events/ScheduledEventPresenterImpl.kt @@ -3,11 +3,10 @@ package org.dhis2.usescases.events import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable -import org.dhis2.data.dhislogic.DhisEventUtils +import org.dhis2.commons.date.DateUtils import org.dhis2.usescases.eventsWithoutRegistration.eventDetails.providers.DEFAULT_MAX_DATE import org.dhis2.usescases.eventsWithoutRegistration.eventDetails.providers.DEFAULT_MIN_DATE import org.dhis2.usescases.eventsWithoutRegistration.eventDetails.providers.InputDateValues -import org.dhis2.utils.DateUtils import org.hisp.dhis.android.core.D2 import org.hisp.dhis.android.core.arch.helpers.UidsHelper import org.hisp.dhis.android.core.category.CategoryOption @@ -25,7 +24,6 @@ class ScheduledEventPresenterImpl( val view: ScheduledEventContract.View, val d2: D2, val eventUid: String, - val eventUtils: DhisEventUtils, ) : ScheduledEventContract.Presenter { private lateinit var disposable: CompositeDisposable @@ -84,11 +82,7 @@ class ScheduledEventPresenterImpl( override fun setEventDate(date: Date) { d2.eventModule().events().uid(eventUid).setEventDate(date) d2.eventModule().events().uid(eventUid).setStatus(EventStatus.ACTIVE) - if (eventUtils.newEventNeedsExtraInfo(eventUid)) { - view.openInitialActivity() - } else { - view.openFormActivity() - } + view.openFormActivity() } override fun formatDateValues(date: InputDateValues): Date { diff --git a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/EventCaptureActivity.kt b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/EventCaptureActivity.kt index b0c1f1338a..d853902964 100644 --- a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/EventCaptureActivity.kt +++ b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/EventCaptureActivity.kt @@ -67,9 +67,8 @@ class EventCaptureActivity : TEIDataActivityContract { private lateinit var binding: ActivityEventCaptureBinding - @JvmField @Inject - var presenter: EventCaptureContract.Presenter? = null + override lateinit var presenter: EventCaptureContract.Presenter @JvmField @Inject @@ -99,8 +98,8 @@ class EventCaptureActivity : eventUid = intent.getStringExtra(Constants.EVENT_UID) programUid = intent.getStringExtra(Constants.PROGRAM_UID) setUpEventCaptureComponent(eventUid) - teiUid = presenter!!.teiUid - enrollmentUid = presenter!!.enrollmentUid + teiUid = presenter.getTeiUid() + enrollmentUid = presenter.getEnrollmentUid() themeManager!!.setProgramTheme(intent.getStringExtra(Constants.PROGRAM_UID)!!) super.onCreate(savedInstanceState) binding = DataBindingUtil.setContentView(this, R.layout.activity_event_capture) @@ -124,8 +123,8 @@ class EventCaptureActivity : } } showProgress() - presenter!!.initNoteCounter() - presenter!!.init() + presenter.initNoteCounter() + presenter.init() binding.syncButton.setOnClickListener { showSyncDialog(EVENT_SYNC) } if (intent.shouldLaunchSyncDialog()) { @@ -227,14 +226,14 @@ class EventCaptureActivity : override fun onResume() { super.onResume() - presenter!!.refreshTabCounters() + presenter.refreshTabCounters() with(dashboardViewModel) { this?.selectedEventUid()?.observe(this@EventCaptureActivity, ::updateLandscapeViewsOnEventChange) } } override fun onDestroy() { - presenter!!.onDettach() + presenter.onDettach() super.onDestroy() } @@ -272,11 +271,11 @@ class EventCaptureActivity : { /*Unused*/ }, - { presenter!!.deleteEvent() }, + { presenter.deleteEvent() }, ) dialog.show(supportFragmentManager, AlertBottomDialog::class.java.simpleName) } else if (isFormScreen()) { - presenter?.emitAction(EventCaptureAction.ON_BACK) + presenter.emitAction(EventCaptureAction.ON_BACK) } else { finishDataEntry() } @@ -292,16 +291,12 @@ class EventCaptureActivity : override fun updatePercentage(primaryValue: Float) { binding.completion.setCompletionPercentage(primaryValue) - if (!presenter!!.completionPercentageVisibility) { + if (!presenter.getCompletionPercentageVisibility()) { binding.completion.visibility = View.GONE } } - override fun showCompleteActions( - canComplete: Boolean, - emptyMandatoryFields: Map, - eventCompletionDialog: EventCompletionDialog, - ) { + override fun showCompleteActions(eventCompletionDialog: EventCompletionDialog) { val dialog = BottomSheetDialog( bottomSheetDialogUiModel = eventCompletionDialog.bottomSheetDialogUiModel, onMainButtonClicked = { @@ -328,15 +323,15 @@ class EventCaptureActivity : } } - override fun SaveAndFinish() { + override fun saveAndFinish() { displayMessage(getString(R.string.saved)) setAction(FormBottomDialog.ActionType.FINISH) } override fun attemptToSkip() { instance - .setAccessDataWrite(presenter!!.canWrite()) - .setIsExpired(presenter!!.hasExpired()) + .setAccessDataWrite(presenter.canWrite()) + .setIsExpired(presenter.hasExpired()) .setSkip(true) .setListener { actionType: FormBottomDialog.ActionType -> setAction(actionType) } .show(supportFragmentManager, SHOW_OPTIONS) @@ -344,8 +339,8 @@ class EventCaptureActivity : override fun attemptToReschedule() { instance - .setAccessDataWrite(presenter!!.canWrite()) - .setIsExpired(presenter!!.hasExpired()) + .setAccessDataWrite(presenter.canWrite()) + .setIsExpired(presenter.hasExpired()) .setReschedule(true) .setListener { actionType: FormBottomDialog.ActionType -> setAction(actionType) } .show(supportFragmentManager, SHOW_OPTIONS) @@ -355,12 +350,12 @@ class EventCaptureActivity : when (actionType) { FormBottomDialog.ActionType.COMPLETE -> { isEventCompleted = true - presenter!!.completeEvent(false) + presenter.completeEvent(false) } - FormBottomDialog.ActionType.COMPLETE_ADD_NEW -> presenter!!.completeEvent(true) + FormBottomDialog.ActionType.COMPLETE_ADD_NEW -> presenter.completeEvent(true) FormBottomDialog.ActionType.FINISH_ADD_NEW -> restartDataEntry() - FormBottomDialog.ActionType.SKIP -> presenter!!.skipEvent() + FormBottomDialog.ActionType.SKIP -> presenter.skipEvent() FormBottomDialog.ActionType.RESCHEDULE -> { // Do nothing } @@ -401,29 +396,15 @@ class EventCaptureActivity : finish() } - override fun renderInitialInfo( - stageName: String, - eventDate: String, - orgUnit: String, - catOption: String, - ) { + override fun renderInitialInfo(stageName: String) { binding.programStageName.text = stageName - val eventDataString = StringBuilder(String.format("%s | %s", eventDate, orgUnit)) - if (catOption.isNotEmpty()) { - eventDataString.append(String.format(" | %s", catOption)) - } - binding.eventSecundaryInfo.text = eventDataString - } - - override fun getPresenter(): EventCaptureContract.Presenter { - return presenter!! } override fun showMoreOptions(view: View) { AppMenuHelper.Builder().menu(this, R.menu.event_menu).anchor(view) .onMenuInflated { popupMenu: PopupMenu -> popupMenu.menu.findItem(R.id.menu_delete).isVisible = - presenter!!.canWrite() && presenter!!.isEnrollmentOpen + presenter.canWrite() && presenter.isEnrollmentOpen() popupMenu.menu.findItem(R.id.menu_share).isVisible = false } .onMenuItemClicked { itemId: Int? -> @@ -448,7 +429,7 @@ class EventCaptureActivity : } private fun confirmDeleteEvent() { - presenter?.programStage().let { + presenter.programStage().let { CustomDialog( this, resourceManager.formatWithEventLabel( @@ -465,7 +446,7 @@ class EventCaptureActivity : object : DialogClickListener { override fun onPositive() { analyticsHelper().setEvent(DELETE_EVENT, CLICK, DELETE_EVENT) - presenter!!.deleteEvent() + presenter.deleteEvent() } override fun onNegative() { @@ -482,7 +463,7 @@ class EventCaptureActivity : .setMessage( resourceManager.formatWithEventLabel( R.string.event_label_date_in_future_message, - programStageUid = presenter?.programStage(), + programStageUid = presenter.programStage(), ), ) .setPositiveButton( diff --git a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/EventCaptureContract.java b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/EventCaptureContract.java deleted file mode 100644 index 0cbab10877..0000000000 --- a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/EventCaptureContract.java +++ /dev/null @@ -1,166 +0,0 @@ -package org.dhis2.usescases.eventsWithoutRegistration.eventCapture; - -import androidx.lifecycle.LiveData; - -import org.dhis2.ui.dialogs.bottomsheet.FieldWithIssue; -import org.dhis2.usescases.eventsWithoutRegistration.eventCapture.model.EventCompletionDialog; -import org.dhis2.usescases.general.AbstractActivityContracts; -import org.hisp.dhis.android.core.common.ValidationStrategy; -import org.hisp.dhis.android.core.event.EventStatus; -import org.hisp.dhis.android.core.organisationunit.OrganisationUnit; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Date; -import java.util.List; -import java.util.Map; - -import io.reactivex.Flowable; -import io.reactivex.Observable; -import io.reactivex.Single; - -public class EventCaptureContract { - - public interface View extends AbstractActivityContracts.View { - - void renderInitialInfo(String stageName, String eventDate, String orgUnit, String catOption); - - EventCaptureContract.Presenter getPresenter(); - - void updatePercentage(float primaryValue); - - void showCompleteActions( - boolean canComplete, - Map emptyMandatoryFields, - EventCompletionDialog eventCompletionDialog); - - void restartDataEntry(); - - void finishDataEntry(); - - void SaveAndFinish(); - - void showSnackBar(int messageId, String programStage); - - void attemptToSkip(); - - void attemptToReschedule(); - - void showEventIntegrityAlert(); - - void updateNoteBadge(int numberOfNotes); - - void goBack(); - - void showProgress(); - - void hideProgress(); - - void showNavigationBar(); - - void hideNavigationBar(); - } - - public interface Presenter extends AbstractActivityContracts.Presenter { - - LiveData observeActions(); - - void init(); - - void onBackClick(); - - void attemptFinish(boolean canComplete, - @Nullable String onCompleteMessage, - List errorFields, - Map emptyMandatoryFields, - List warningFields); - - boolean isEnrollmentOpen(); - - void completeEvent(boolean addNew); - - void deleteEvent(); - - void skipEvent(); - - void rescheduleEvent(Date time); - - boolean canWrite(); - - boolean hasExpired(); - - void initNoteCounter(); - - void refreshTabCounters(); - - void hideProgress(); - - void showProgress(); - - boolean getCompletionPercentageVisibility(); - - void emitAction(@NotNull EventCaptureAction onBack); - - String programStage(); - - @Nullable - String getTeiUid(); - - @Nullable - String getEnrollmentUid(); - } - - public interface EventCaptureRepository { - - Flowable eventIntegrityCheck(); - - Flowable programStageName(); - - Flowable eventDate(); - - Flowable orgUnit(); - - Flowable catOption(); - - Observable completeEvent(); - - Flowable eventStatus(); - - boolean isEnrollmentOpen(); - - Observable deleteEvent(); - - Observable updateEventStatus(EventStatus skipped); - - Observable rescheduleEvent(Date time); - - Observable programStage(); - - boolean getAccessDataWrite(); - - boolean isEnrollmentCancelled(); - - boolean isEventEditable(String eventUid); - - Single canReOpenEvent(); - - Observable isCompletedEventExpired(String eventUid); - - Single getNoteCount(); - - boolean showCompletionPercentage(); - - boolean hasAnalytics(); - - boolean hasRelationships(); - - ValidationStrategy validationStrategy(); - - @Nullable - String getTeiUid(); - - @Nullable - String getEnrollmentUid(); - } - -} diff --git a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/EventCaptureContract.kt b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/EventCaptureContract.kt new file mode 100644 index 0000000000..a90a5c914d --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/EventCaptureContract.kt @@ -0,0 +1,93 @@ +package org.dhis2.usescases.eventsWithoutRegistration.eventCapture + +import androidx.lifecycle.LiveData +import io.reactivex.Flowable +import io.reactivex.Observable +import io.reactivex.Single +import org.dhis2.form.model.EventMode +import org.dhis2.ui.dialogs.bottomsheet.FieldWithIssue +import org.dhis2.usescases.eventsWithoutRegistration.eventCapture.model.EventCompletionDialog +import org.dhis2.usescases.general.AbstractActivityContracts +import org.hisp.dhis.android.core.common.ValidationStrategy +import org.hisp.dhis.android.core.event.EventStatus +import org.hisp.dhis.android.core.organisationunit.OrganisationUnit +import java.util.Date + +class EventCaptureContract { + interface View : AbstractActivityContracts.View { + fun renderInitialInfo(stageName: String) + val presenter: Presenter + fun updatePercentage(primaryValue: Float) + fun showCompleteActions(eventCompletionDialog: EventCompletionDialog) + + fun restartDataEntry() + fun finishDataEntry() + fun saveAndFinish() + fun showSnackBar(messageId: Int, programStage: String) + fun attemptToSkip() + fun attemptToReschedule() + fun showEventIntegrityAlert() + fun updateNoteBadge(numberOfNotes: Int) + fun goBack() + fun showProgress() + fun hideProgress() + fun showNavigationBar() + fun hideNavigationBar() + } + + interface Presenter : AbstractActivityContracts.Presenter { + fun observeActions(): LiveData + fun init() + fun onBackClick() + fun attemptFinish( + canComplete: Boolean, + onCompleteMessage: String?, + errorFields: List, + emptyMandatoryFields: Map, + warningFields: List, + eventMode: EventMode? = null, + ) + + fun isEnrollmentOpen(): Boolean + fun completeEvent(addNew: Boolean) + fun deleteEvent() + fun skipEvent() + fun rescheduleEvent(time: Date) + fun canWrite(): Boolean + fun hasExpired(): Boolean + fun initNoteCounter() + fun refreshTabCounters() + fun hideProgress() + fun showProgress() + fun getCompletionPercentageVisibility(): Boolean + fun emitAction(onBack: EventCaptureAction) + fun programStage(): String + fun getTeiUid(): String? + fun getEnrollmentUid(): String? + } + + interface EventCaptureRepository { + fun eventIntegrityCheck(): Flowable + fun programStageName(): Flowable + fun orgUnit(): Flowable + fun completeEvent(): Observable + fun eventStatus(): Flowable + val isEnrollmentOpen: Boolean + fun deleteEvent(): Observable + fun updateEventStatus(skipped: EventStatus): Observable + fun rescheduleEvent(time: Date): Observable + fun programStage(): Observable + val accessDataWrite: Boolean + val isEnrollmentCancelled: Boolean + fun isEventEditable(eventUid: String): Boolean + fun canReOpenEvent(): Single + fun isCompletedEventExpired(eventUid: String): Observable + val noteCount: Single + fun showCompletionPercentage(): Boolean + fun hasAnalytics(): Boolean + fun hasRelationships(): Boolean + fun validationStrategy(): ValidationStrategy + fun getTeiUid(): String? + fun getEnrollmentUid(): String? + } +} diff --git a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/EventCapturePresenterImpl.kt b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/EventCapturePresenterImpl.kt index 51c2072858..1ee0a6c4a3 100644 --- a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/EventCapturePresenterImpl.kt +++ b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/EventCapturePresenterImpl.kt @@ -7,17 +7,19 @@ import io.reactivex.Flowable import io.reactivex.disposables.CompositeDisposable import io.reactivex.processors.PublishProcessor import org.dhis2.R -import org.dhis2.bindings.canSkipErrorFix import org.dhis2.commons.prefs.Preference import org.dhis2.commons.prefs.PreferenceProvider import org.dhis2.commons.schedulers.SchedulerProvider import org.dhis2.commons.schedulers.defaultSubscribe +import org.dhis2.form.data.EventRepository +import org.dhis2.form.model.EventMode import org.dhis2.ui.dialogs.bottomsheet.FieldWithIssue import org.dhis2.usescases.eventsWithoutRegistration.EventIdlingResourceSingleton import org.dhis2.usescases.eventsWithoutRegistration.eventCapture.EventCaptureContract.EventCaptureRepository import org.dhis2.usescases.eventsWithoutRegistration.eventCapture.domain.ConfigureEventCompletionDialog import org.dhis2.usescases.eventsWithoutRegistration.eventCapture.model.EventCaptureInitialInfo import org.hisp.dhis.android.core.common.Unit +import org.hisp.dhis.android.core.common.ValidationStrategy import org.hisp.dhis.android.core.event.EventStatus import timber.log.Timber import java.util.Date @@ -56,9 +58,7 @@ class EventCapturePresenterImpl( compositeDisposable.add( Flowable.zip( eventCaptureRepository.programStageName(), - eventCaptureRepository.eventDate(), eventCaptureRepository.orgUnit(), - eventCaptureRepository.catOption(), ::EventCaptureInitialInfo, ).defaultSubscribe( schedulerProvider, @@ -69,9 +69,6 @@ class EventCapturePresenterImpl( ) view.renderInitialInfo( initialInfo.programStageName, - initialInfo.eventDate, - initialInfo.organisationUnit.displayName(), - initialInfo.categoryOption, ) }, Timber::e, @@ -114,15 +111,21 @@ class EventCapturePresenterImpl( errorFields: List, emptyMandatoryFields: Map, warningFields: List, + eventMode: EventMode?, ) { val eventStatus = eventStatus if (eventStatus != EventStatus.ACTIVE) { setUpActionByStatus(eventStatus) } else { - val validationStrategy = eventCaptureRepository.validationStrategy() - val canSkipErrorFix = validationStrategy.canSkipErrorFix( + val canSkipErrorFix = canSkipErrorFix( hasErrorFields = errorFields.isNotEmpty(), hasEmptyMandatoryFields = emptyMandatoryFields.isNotEmpty(), + hasEmptyEventCreationMandatoryFields = with(emptyMandatoryFields) { + containsValue(EventRepository.EVENT_DETAILS_SECTION_UID) || + containsValue(EventRepository.EVENT_CATEGORY_COMBO_SECTION_UID) + }, + eventMode = eventMode, + validationStrategy = eventCaptureRepository.validationStrategy(), ) val eventCompletionDialog = configureEventCompletionDialog.invoke( errorFields, @@ -132,20 +135,32 @@ class EventCapturePresenterImpl( onCompleteMessage, canSkipErrorFix, ) - view.showCompleteActions( - canComplete && eventCaptureRepository.isEnrollmentOpen, - emptyMandatoryFields, - eventCompletionDialog, - ) + view.showCompleteActions(eventCompletionDialog) } view.showNavigationBar() } + private fun canSkipErrorFix( + hasErrorFields: Boolean, + hasEmptyMandatoryFields: Boolean, + hasEmptyEventCreationMandatoryFields: Boolean, + eventMode: EventMode?, + validationStrategy: ValidationStrategy, + ): Boolean { + return when (validationStrategy) { + ValidationStrategy.ON_COMPLETE -> when (eventMode) { + EventMode.NEW -> !hasEmptyEventCreationMandatoryFields + else -> true + } + ValidationStrategy.ON_UPDATE_AND_INSERT -> !hasErrorFields && !hasEmptyMandatoryFields + } + } + private fun setUpActionByStatus(eventStatus: EventStatus) { when (eventStatus) { EventStatus.COMPLETED -> if (!hasExpired && !eventCaptureRepository.isEnrollmentCancelled) { - view.SaveAndFinish() + view.saveAndFinish() } else { view.finishDataEntry() } @@ -163,11 +178,13 @@ class EventCapturePresenterImpl( } override fun completeEvent(addNew: Boolean) { + EventIdlingResourceSingleton.increment() compositeDisposable.add( eventCaptureRepository.completeEvent() .defaultSubscribe( schedulerProvider, - { + onNext = { + EventIdlingResourceSingleton.decrement() if (addNew) { view.restartDataEntry() } else { @@ -175,7 +192,10 @@ class EventCapturePresenterImpl( view.finishDataEntry() } }, - Timber::e, + onError = { + EventIdlingResourceSingleton.decrement() + Timber.e(it) + }, ), ) } @@ -282,9 +302,9 @@ class EventCapturePresenterImpl( override fun programStage(): String = eventCaptureRepository.programStage().blockingFirst() override fun getEnrollmentUid(): String? { - return eventCaptureRepository.enrollmentUid + return eventCaptureRepository.getEnrollmentUid() } override fun getTeiUid(): String? { - return eventCaptureRepository.teiUid + return eventCaptureRepository.getTeiUid() } } diff --git a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/EventCaptureRepositoryImpl.java b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/EventCaptureRepositoryImpl.java index 7111c4bf5f..2a0e94832c 100644 --- a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/EventCaptureRepositoryImpl.java +++ b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/EventCaptureRepositoryImpl.java @@ -2,7 +2,6 @@ import org.dhis2.commons.bindings.SdkExtensionsKt; import org.dhis2.data.dhislogic.AuthoritiesKt; -import org.dhis2.utils.DateUtils; import org.hisp.dhis.android.core.D2; import org.hisp.dhis.android.core.common.BaseIdentifiableObject; import org.hisp.dhis.android.core.common.ValidationStrategy; @@ -72,31 +71,17 @@ public Flowable programStageName() { } @Override - public Flowable eventDate() { - Event currentEvent = getCurrentEvent(); + public Flowable orgUnit() { return Flowable.just( - currentEvent.eventDate() != null ? DateUtils.uiDateFormat().format(currentEvent.eventDate()) : "" + Objects.requireNonNull( + d2.organisationUnitModule() + .organisationUnits() + .uid(getCurrentEvent().organisationUnit()) + .blockingGet() + ) ); } - @Override - public Flowable orgUnit() { - return Flowable.just(d2.organisationUnitModule().organisationUnits().uid(getCurrentEvent().organisationUnit()).blockingGet()); - } - - - @Override - public Flowable catOption() { - return Flowable.just(d2.categoryModule().categoryOptionCombos().uid(getCurrentEvent().attributeOptionCombo())) - .map(categoryOptionComboRepo -> { - if (categoryOptionComboRepo.blockingGet() == null) - return ""; - else - return categoryOptionComboRepo.blockingGet().displayName(); - }) - .map(displayName -> displayName.equals("default") ? "" : displayName); - } - @Override public Observable completeEvent() { return Observable.fromCallable(() -> { @@ -162,8 +147,8 @@ public Single canReOpenEvent() { @Override public Observable isCompletedEventExpired(String eventUid) { return d2.eventModule().eventService().getEditableStatus(eventUid).map(editionStatus -> { - if (editionStatus instanceof EventEditableStatus.NonEditable) { - return ((EventEditableStatus.NonEditable) editionStatus).getReason() == EventNonEditableReason.EXPIRED; + if (editionStatus instanceof EventEditableStatus.NonEditable nonEditableStatus) { + return nonEditableStatus.getReason() == EventNonEditableReason.EXPIRED; } else { return false; } diff --git a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/domain/ConfigureEventCompletionDialog.kt b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/domain/ConfigureEventCompletionDialog.kt index c85784aa50..3543b4641b 100644 --- a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/domain/ConfigureEventCompletionDialog.kt +++ b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/domain/ConfigureEventCompletionDialog.kt @@ -96,7 +96,7 @@ class ConfigureEventCompletionDialog( WARNING, SUCCESSFUL, -> EventCompletionButtons( - CompleteButton(), + CompleteButton, FormBottomDialog.ActionType.COMPLETE, ) } diff --git a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/eventCaptureFragment/EventCaptureFormFragment.java b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/eventCaptureFragment/EventCaptureFormFragment.java index f3915a35b3..0e00f1c11e 100644 --- a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/eventCaptureFragment/EventCaptureFormFragment.java +++ b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/eventCaptureFragment/EventCaptureFormFragment.java @@ -96,7 +96,7 @@ public void onCreate(@Nullable @org.jetbrains.annotations.Nullable Bundle savedI return Unit.INSTANCE; }) .onDataIntegrityResult(result -> { - presenter.handleDataIntegrityResult(result); + presenter.handleDataIntegrityResult(result, eventMode); return Unit.INSTANCE; }) .factory(activity.getSupportFragmentManager()) diff --git a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/eventCaptureFragment/EventCaptureFormPresenter.kt b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/eventCaptureFragment/EventCaptureFormPresenter.kt index b3d8625fc3..4b491b27c1 100644 --- a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/eventCaptureFragment/EventCaptureFormPresenter.kt +++ b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/eventCaptureFragment/EventCaptureFormPresenter.kt @@ -13,6 +13,7 @@ import org.dhis2.form.data.FieldsWithWarningResult import org.dhis2.form.data.MissingMandatoryResult import org.dhis2.form.data.NotSavedResult import org.dhis2.form.data.SuccessfulResult +import org.dhis2.form.model.EventMode import org.dhis2.usescases.eventsWithoutRegistration.EventIdlingResourceSingleton import org.dhis2.usescases.eventsWithoutRegistration.eventCapture.EventCaptureContract import org.dhis2.usescases.eventsWithoutRegistration.eventCapture.domain.ReOpenEventUseCase @@ -32,7 +33,7 @@ class EventCaptureFormPresenter( private val dispatcherProvider: DispatcherProvider, ) { - fun handleDataIntegrityResult(result: DataIntegrityCheckResult) { + fun handleDataIntegrityResult(result: DataIntegrityCheckResult, eventMode: EventMode? = null) { when (result) { is FieldsWithErrorResult -> activityPresenter.attemptFinish( result.canComplete, @@ -40,6 +41,7 @@ class EventCaptureFormPresenter( result.fieldUidErrorList, result.mandatoryFields, result.warningFields, + eventMode, ) is FieldsWithWarningResult -> activityPresenter.attemptFinish( @@ -48,6 +50,7 @@ class EventCaptureFormPresenter( emptyList(), emptyMap(), result.fieldUidWarningList, + eventMode, ) is MissingMandatoryResult -> activityPresenter.attemptFinish( @@ -56,6 +59,7 @@ class EventCaptureFormPresenter( result.errorFields, result.mandatoryFields, result.warningFields, + eventMode, ) is SuccessfulResult -> activityPresenter.attemptFinish( @@ -96,7 +100,7 @@ class EventCaptureFormPresenter( EventNonEditableReason.EVENT_DATE_IS_NOT_IN_ORGUNIT_RANGE -> resourceManager.getString(R.string.event_date_not_in_orgunit_range) to false EventNonEditableReason.NO_CATEGORY_COMBO_ACCESS -> resourceManager.getString(R.string.edition_no_catcombo_access) to false EventNonEditableReason.ENROLLMENT_IS_NOT_OPEN -> resourceManager.formatWithEnrollmentLabel( - null, + d2.eventModule().events().uid(eventUid).blockingGet()?.program(), R.string.edition_enrollment_is_no_open_V2, 1, ) to false diff --git a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/eventInitialFragment/EventCaptureInitialComponent.kt b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/eventInitialFragment/EventCaptureInitialComponent.kt deleted file mode 100644 index c2dc482ebc..0000000000 --- a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/eventInitialFragment/EventCaptureInitialComponent.kt +++ /dev/null @@ -1,8 +0,0 @@ -package org.dhis2.usescases.eventsWithoutRegistration.eventCapture.eventInitialFragment - -import dagger.Subcomponent - -@Subcomponent(modules = [EventCaptureInitialModule::class]) -interface EventCaptureInitialComponent { - fun inject(fragment: EventCaptureInitialFragment) -} diff --git a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/eventInitialFragment/EventCaptureInitialFragment.kt b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/eventInitialFragment/EventCaptureInitialFragment.kt deleted file mode 100644 index ee69557db7..0000000000 --- a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/eventInitialFragment/EventCaptureInitialFragment.kt +++ /dev/null @@ -1,5 +0,0 @@ -package org.dhis2.usescases.eventsWithoutRegistration.eventCapture.eventInitialFragment - -import org.dhis2.usescases.general.FragmentGlobalAbstract - -class EventCaptureInitialFragment : FragmentGlobalAbstract() diff --git a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/eventInitialFragment/EventCaptureInitialModule.kt b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/eventInitialFragment/EventCaptureInitialModule.kt deleted file mode 100644 index 3a5d2ef621..0000000000 --- a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/eventInitialFragment/EventCaptureInitialModule.kt +++ /dev/null @@ -1,6 +0,0 @@ -package org.dhis2.usescases.eventsWithoutRegistration.eventCapture.eventInitialFragment - -import dagger.Module - -@Module -class EventCaptureInitialModule diff --git a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/model/EventCaptureInitialInfo.kt b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/model/EventCaptureInitialInfo.kt index cb20466399..aee1cf565d 100644 --- a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/model/EventCaptureInitialInfo.kt +++ b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/model/EventCaptureInitialInfo.kt @@ -4,7 +4,5 @@ import org.hisp.dhis.android.core.organisationunit.OrganisationUnit data class EventCaptureInitialInfo( val programStageName: String, - val eventDate: String, val organisationUnit: OrganisationUnit, - val categoryOption: String, ) diff --git a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventDetails/data/EventDetailsRepository.kt b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventDetails/data/EventDetailsRepository.kt index 29dcda480d..a70ef1c12c 100644 --- a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventDetails/data/EventDetailsRepository.kt +++ b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventDetails/data/EventDetailsRepository.kt @@ -3,11 +3,12 @@ package org.dhis2.usescases.eventsWithoutRegistration.eventDetails.data import io.reactivex.Observable import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow +import org.dhis2.commons.data.EventCreationType +import org.dhis2.commons.date.DateUtils import org.dhis2.data.dhislogic.AUTH_ALL import org.dhis2.data.dhislogic.AUTH_UNCOMPLETE_EVENT import org.dhis2.form.model.FieldUiModel import org.dhis2.form.ui.FieldViewModelFactory -import org.dhis2.utils.DateUtils import org.hisp.dhis.android.core.D2 import org.hisp.dhis.android.core.arch.repositories.scope.RepositoryScope import org.hisp.dhis.android.core.category.CategoryCombo @@ -36,6 +37,7 @@ class EventDetailsRepository( private val eventUid: String?, private val programStageUid: String?, private val fieldFactory: FieldViewModelFactory?, + private val eventCreationType: EventCreationType, private val onError: (Throwable) -> String?, ) { @@ -148,10 +150,15 @@ class EventDetailsRepository( return d2.organisationUnitModule().organisationUnits() .byProgramUids(listOf(programUid)) .byParentUid().eq(parentUid) - .byOrganisationUnitScope(OrganisationUnit.Scope.SCOPE_DATA_CAPTURE) + .byOrganisationUnitScope(getOrgUnitScope()) .blockingGet() } + private fun getOrgUnitScope() = when (eventCreationType) { + EventCreationType.REFERAL -> OrganisationUnit.Scope.SCOPE_TEI_SEARCH + else -> OrganisationUnit.Scope.SCOPE_DATA_CAPTURE + } + fun getOrganisationUnit(orgUnitUid: String): OrganisationUnit? { return d2.organisationUnitModule().organisationUnits() .byUid() @@ -160,10 +167,18 @@ class EventDetailsRepository( } fun getOrganisationUnits(): List { - return d2.organisationUnitModule().organisationUnits() - .byOrganisationUnitScope(OrganisationUnit.Scope.SCOPE_DATA_CAPTURE) - .byProgramUids(listOf(programUid)) - .blockingGet() + val scope = getOrgUnitScope() + return when (scope) { + OrganisationUnit.Scope.SCOPE_DATA_CAPTURE -> + d2.organisationUnitModule().organisationUnits() + .byOrganisationUnitScope(OrganisationUnit.Scope.SCOPE_DATA_CAPTURE) + .byProgramUids(listOf(programUid)) + .blockingGet() + OrganisationUnit.Scope.SCOPE_TEI_SEARCH -> + d2.organisationUnitModule().organisationUnits() + .byProgramUids(listOf(programUid)) + .blockingGet() + } } fun getGeometryModel(): FieldUiModel { diff --git a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventDetails/domain/ConfigureEventDetails.kt b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventDetails/domain/ConfigureEventDetails.kt index 300ad092f4..4026469dd4 100644 --- a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventDetails/domain/ConfigureEventDetails.kt +++ b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventDetails/domain/ConfigureEventDetails.kt @@ -5,6 +5,7 @@ import kotlinx.coroutines.flow.flowOf import org.dhis2.commons.data.EventCreationType import org.dhis2.commons.data.EventCreationType.REFERAL import org.dhis2.commons.resources.MetadataIconProvider +import org.dhis2.ui.toColor import org.dhis2.usescases.eventsWithoutRegistration.eventDetails.data.EventDetailsRepository import org.dhis2.usescases.eventsWithoutRegistration.eventDetails.models.EventDetails import org.dhis2.usescases.eventsWithoutRegistration.eventDetails.providers.EventDetailResourcesProvider @@ -14,6 +15,7 @@ import org.hisp.dhis.android.core.event.Event import org.hisp.dhis.android.core.event.EventEditableStatus.Editable import org.hisp.dhis.android.core.event.EventEditableStatus.NonEditable import org.hisp.dhis.android.core.event.EventStatus.OVERDUE +import org.hisp.dhis.mobile.ui.designsystem.theme.SurfaceColor import java.util.Date class ConfigureEventDetails( @@ -40,11 +42,17 @@ class ConfigureEventDetails( ) val storedEvent = repository.getEvent() val programStage = repository.getProgramStage() + val program = repository.getProgram() return flowOf( EventDetails( name = programStage?.displayName(), description = programStage?.displayDescription(), - metadataIconData = programStage?.style()?.let { metadataIconProvider(programStage.style()) }, + metadataIconData = programStage?.style()?.let { + metadataIconProvider( + programStage.style(), + program?.style()?.color()?.toColor() ?: SurfaceColor.Primary, + ) + }, enabled = isEnable(storedEvent), isEditable = isEditable(), editableReason = getEditableReason(), diff --git a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventDetails/injection/EventDetailsModule.kt b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventDetails/injection/EventDetailsModule.kt index dafd987db5..daf4ee8309 100644 --- a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventDetails/injection/EventDetailsModule.kt +++ b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventDetails/injection/EventDetailsModule.kt @@ -83,6 +83,7 @@ class EventDetailsModule( programUid = programUid, eventUid = eventUid, programStageUid = programStageUid, + eventCreationType = eventCreationType, fieldFactory = FieldViewModelFactoryImpl( UiStyleProviderImpl( FormUiModelColorFactoryImpl(context, colorUtils), diff --git a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventDetails/ui/EventDetailsFragment.kt b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventDetails/ui/EventDetailsFragment.kt index 3ca3f39d94..036a3fe9f7 100644 --- a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventDetails/ui/EventDetailsFragment.kt +++ b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventDetails/ui/EventDetailsFragment.kt @@ -239,7 +239,11 @@ class EventDetailsFragment : FragmentGlobalAbstract() { detailsEnabled = details.enabled, onDateClick = {}, onDateSelected = { dateValues -> - viewModel.onDateSet(dateValues.year, dateValues.month - 1, dateValues.day) + viewModel.onDateSet( + dateValues.year, + dateValues.month - 1, + dateValues.day, + ) }, onClear = { viewModel.onClearEventReportDate() }, required = true, @@ -298,7 +302,10 @@ class EventDetailsFragment : FragmentGlobalAbstract() { ) } } else if (!catCombo.isDefault) { - ProvideEmptyCategorySelector(name = catCombo.displayName ?: getString(R.string.cat_combo), option = getString(R.string.no_options)) + ProvideEmptyCategorySelector( + name = catCombo.displayName ?: getString(R.string.cat_combo), + option = getString(R.string.no_options), + ) } ProvideCoordinates( coordinates = coordinates, @@ -339,7 +346,20 @@ class EventDetailsFragment : FragmentGlobalAbstract() { ) .singleSelection() .orgUnitScope( - OrgUnitSelectorScope.ProgramCaptureScope(viewModel.eventOrgUnit.value.programUid!!), + when (getEventCreationType(requireArguments().getString(EVENT_CREATION_TYPE))) { + EventCreationType.REFERAL -> + OrgUnitSelectorScope.ProgramSearchScope( + viewModel.eventOrgUnit.value.programUid!!, + ) + + EventCreationType.DEFAULT, + EventCreationType.ADDNEW, + EventCreationType.SCHEDULE, + -> + OrgUnitSelectorScope.ProgramCaptureScope( + viewModel.eventOrgUnit.value.programUid!!, + ) + }, ) .onSelection { selectedOrgUnits -> viewModel.setUpOrgUnit(selectedOrgUnit = selectedOrgUnits.firstOrNull()?.uid()) diff --git a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventInitial/EventInitialRepositoryImpl.java b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventInitial/EventInitialRepositoryImpl.java index 78c2b7adf3..010541dad7 100644 --- a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventInitial/EventInitialRepositoryImpl.java +++ b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventInitial/EventInitialRepositoryImpl.java @@ -3,13 +3,12 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import org.dhis2.R; import org.dhis2.commons.resources.MetadataIconProvider; import org.dhis2.data.forms.FormSectionViewModel; -import org.dhis2.mobileProgramRules.RuleEngineHelper; import org.dhis2.form.model.FieldUiModel; import org.dhis2.form.model.OptionSetConfiguration; import org.dhis2.form.ui.FieldViewModelFactory; +import org.dhis2.mobileProgramRules.RuleEngineHelper; import org.dhis2.ui.MetadataIconData; import org.dhis2.utils.Result; import org.hisp.dhis.android.core.D2; @@ -59,7 +58,6 @@ public class EventInitialRepositoryImpl implements EventInitialRepository { private final String stageUid; private final MetadataIconProvider metadataIconProvider; - EventInitialRepositoryImpl(String eventUid, String stageUid, D2 d2, @@ -303,9 +301,9 @@ public Flowable> list() { @Override public Flowable> calculate() { - if(ruleEngineHelper!=null) { + if (ruleEngineHelper != null) { return Flowable.just(ruleEngineHelper.evaluate()).map(Result::success); - }else{ + } else { return Flowable.just(Result.success(new ArrayList<>())); } } @@ -323,6 +321,7 @@ private FieldUiModel transform(@NonNull ProgramStageDataElement stage, DataEleme String formName = dataElement.displayFormName(); String description = dataElement.displayDescription(); + OptionSetConfiguration optionSetConfig = null; if (optionSet != null) { List