Improvements

Fix what the rewrite must not inherit: unsafe secrets and PII flows, sync paths that lose data, a chatty multi-service client and year-by-year copy-paste. Each row is a problem seen in today's code with a concrete fix, rated by impact (H/M/L) and effort (S/M/L).

Rows marked Now are worth doing in the current app before the rewrite starts; they are repeated as the hygiene hotfix in the roadmap.

Product and UX

Problem todayImprovementImpactEffort
Farmer-hub tiles are greyed by alpha only, stay clickable and answer with a toast ("update category", "authenticate land")Show each tile's state and the reason on the tile ("Locked: land not authenticated"), with a button to the fixHS
Validation is toast-only; the user fixes one error, presses Save, then sees the nextInline field errors, all shown at once, with a summary at the top of long formsHM
Category freeze rules (gapStartDate vs admin date vs land authentication vs multi-edit flag) are invisibleOne "Category locked because …" message driven by the same rule the server appliesMS
Crop Actual month copy runs silently on open behind a blocking dialogExplicit "Copy last month" action with a preview of what will be copiedMM
Saving a compact block under 5 acres silently turns into "delete block" through a look-alike dialogSeparate, explicit Delete action; live 5-acre progress bar while picking tiles; explain why a tile is not selectableHS
Land math is hidden: users see "Land limit exceed" with no numbersShow total, minus Crop Actual, minus ATM, minus leased out, equals available, in ATM, Crop Actual and land editHS
Many non-dismissable dialogs (automatic time, session expired, device conflict) stack upOne "Blocking issues" screen listing what must be fixed and howMM
Logout warns "un-synced data will be lost" without numbersShow the pending count, offer "Sync now", and block logout while unsynced items exist unless confirmed twiceHS
Geofence capture shows no accuracy, area or point countLive accuracy in metres, point count, area, perimeter, undo last point; warn when area differs from declared acresHM
Activity Tracker 18:00 edit and submit rules fail silentlyShow the window on screen with a countdownLS
Analytics screen is empty unless data was fetched todayShow the last data with a "last updated" labelMS
Each agricultural year is a copied screen (ActionPlanFragment2025To2026, FarmerAbstract2425BottomSheet)Schema-driven forms: the App Service sends the season's form definition and ceilingsHL
Crop Plan (2023-24) and AAP 2024-25 are hidden but still in codeRetire them; re-specify only if the programme asksMS
Family details (marital status, spouse, father) are captured only for women; father details are saved in spouse columnsCapture family for everyone with separate spouse and father fields, confirmed with the programmeMM
Max land and crop acreage is 99.99 because of a 2-digit input filterServer-configured limits per fieldMS

Data and offline

Problem todayImprovementImpactEffort
Two databases (agri_db and legacy ie_register.db) hold the same farmers, land and crops; bridge mappers copy between themOne database, one row per entity, screens read through queriesHL
Three farmer shapes joined by four different keysOne farmer table: local UUID primary key, unique nullable server id, foreign keys by local idHM
BaseEntity.appId defaults to a random UUID on parsed server data, so re-downloads can duplicate rows; child label tables are never clearedUpsert by server id; replace child collections in one transactionHM
insert(List) is IGNORE while insert(T) is REPLACE: compact blocks never refresh, farmer rows lose device-only flagsExplicit @Upsert (or drift insertOnConflictUpdate) with column-level merge rulesHS
No foreign keys and almost no indexes on join columnsForeign keys plus indexes on every join and filter column; sync_status.sync_key uniqueMS
Queries use LIKE for equality, substring match on asset codes, TEXT date orderingUse =, exact code match, INTEGER epoch millis everywhereMS
JSON blobs in columns (AAP, ATM crops and harvests, change log, practices)Child tables for ATM crops and harvests; keep only opaque survey answers as JSONMM
Year-specific columns (annualActionPlanData2526, NFCategory2526)farmer_plan(farmer_id, plan_year, data)MS
Three error stores and two notification storesOne sync_error table and one notification tableLS
Main-thread queries allowed on two databasesSuspend and Flow DAOs only (or drift isolates)HM
62 schema versions with exports missing for 10-51; destructive fallbacksFresh schema at v1 with exported, tested migrations from day one; drain the old app firstHM

Sync

Problem todayImprovementImpactEffort
Two queues (Room sync_ledger feeding the Bridge SYNC_QUEUE_DB) plus per-row syncStatus that can disagreeOne outbox table in the same DB as the data, written in the same transactionHM
Some writes skip the ledger (ATM harvest, AAP 2025-26), so they are invisible in Pending Sync and have no dependenciesEvery write goes through the outboxHS
Client resolves parent server ids through 40 response handlers and a dependency graphClient sends client UUIDs; the App Service resolves referencesHM
No conflict detection; full DTOs overwrite the serverBase version on every update; typed conflict responses; a per-entity policy (see App Service)HM
Retries up to about 1,176 attempts at a constant delay; handler exceptions drop operations silentlyExponential backoff, a visible "failed" state with reason, manual retry, never silent dropsHS
The queue can be drained by the SyncAdapter process and in-app force sync at onceSingle unique WorkManager job; drop SyncAdapter, stub provider and the :sync processHS
Down-sync can overwrite pending editsNever overwrite a row that has a pending outbox op; merge after the op settlesHS
Full re-download on "load more" and per-farmer refreshSnapshot once, then cursor-based deltas with tombstonesHM
Images that already exist on the server are downloaded and re-uploaded by the workerUpload files once, reference by file idMS
Firebase Storage fallback stores images the backend never learns aboutRemove; resumable uploads to the App ServiceMS

Backend and API

Problem todayImprovementImpactEffort
The app calls about 12 services and 40 datasets, building dataset params and SQL-like criteria= on the phoneOne App Service as the only backend; dataset names and query syntax stay server-sideHL
About 300 calls for a 1,500-farmer setup; about 15 calls on every Home openHydrated farmer snapshot pages and one ETag'd config bundleHM
"Opening" a farmer POSTs to create the server IE from the flat recordServer does get-or-create behind GET /farmers/{id}MS
Season and year are baked into endpoints (AAP2025_26, …2425, IsKAP202324)?season=current; the App Service maps to the right backendHM
POST used as a read (line-items/ownerEntities/latest)Proper GET in the App ServiceLS
HTTP 206 used as "more pages" for ref dataStandard cursor paginationLS
No app-version, tenant or device headers, so the server cannot gate versionsStandard headers and a server-enforced minimum versionHS
Debug builds send ap.gov users to the production hostBuild flavors per environment; one App Service URL per environmentHS

Security and privacy

Problem todayImprovementImpactEffort
Release keystore and passwords committed; Nexus credentials in build files (Now)Rotate the upload key (Play App Signing), rotate Nexus credentials, purge history, move secrets to CIHS
Sync operations with session headers and PII go to Firestore; the whole queue goes to sync-service every 6 hours (Now)Stop both; send redacted counters onlyHS
HttpLoggingInterceptor at BODY level in release (Now)Gate logging to debug builds; one logging facade with redactionHS
Password stored in plaintext in AccountManager and used for silent re-loginAccess and refresh tokens only, refresh token in Keystore-backed storageHM
Aadhaar, ration card and phone numbers unencrypted in RoomSQLCipher database; mask Aadhaar in UI; ask whether the server can return only the last 4 digitsHM
Support export zips the whole DB and prefs (with the session token) to e-mail or external storageRedacted diagnostics uploaded to the App Service, never e-mailedHS
Geofence KML embeds farmer name and villageIds only inside filesMS
Client-held FCM legacy server keyServer-side push via FCM HTTP v1HS
Exported StubContentProvider, broad FileProvider roots, implicit sync-failure broadcastsRemove the provider and broadcasts; narrow FileProvider pathsMS
APP_KEY shipped in resourcesDrop from the client; use Play Integrity at the App Service if app attestation mattersMS
A hard-coded static request header on farmer create/patch (data/.../entity/IE.kt)Review with the backend team and removeMS
Device binding is skipped when offline or on CancelBind refresh tokens to the device on the serverMM
R8 off in releaseR8 and resource shrinking on, with keep rules testedMS

Code quality and testing

Problem todayImprovementImpactEffort
9 tests; about 0% coverage of rules, sync and migrationsUnit tests for every gate rule, validator and land-usage calculation; outbox and migration tests; contract tests against the App ServiceHM
Business rules live in fragments and a 1,970-line AppUtils; 3,395-line DataRepositoryA domain module of pure functions (category gate, land usage, validators) and per-feature repositoriesHM
Land availability computed three ways in three placesOne shared land-usage service, mirrored on the serverHS
Roles checked by substring (contains("fmt_l1")) and config by string code everywhereTyped role enum and typed config objectMS
No DI; Executors, raw threads and AsyncTaskHilt or Koin; coroutines and Flow onlyMS
About 8% duplicated lines (two download view models, two data-setup activities)One generic sync-item engine with role-specific item listsMM
Startup state spread over 5 activities and several pref flagsOne persisted state machine: NoLanguage → NoAccount → NoRole → NeedsWorkNode → Downloading → ReadyMS
Asset name string used as a foreign key by portfolio, ATM and geofenceReference assets by id or UUIDMS
No CI in the repoCI with lint, tests, R8 build and signed artifactsMS

Performance on low-end devices

Problem todayImprovementImpactEffort
Room queries in adapters' bind() and on the main threadPaged lists from Flow queries; no DB work on the main threadHM
Missing indexes make farmer, land and portfolio lookups scanIndexes on join columns (see Data)MS
No R8, no Baseline ProfilesR8 plus Baseline Profiles for cold start and list scrollMS
Photos compressed with an integer-division bug (100 px wide) or to about 45 KB in other placesOne image pipeline: fixed long edge (for example 1280 px), JPEG quality tuned per use, EXIF strippedMS
Many periodic workers (1 h ref data, 1 h comments, 15 min endorsements, 6 h and 12 h telemetry)One sync job plus push; config by ETag on app startMS
Map tiles fetched online during capturePre-cache tiles for the user's villages over Wi-FiMM

Localization

Problem todayImprovementImpactEffort
Tamil missing 416 strings (40%), Telugu missing 48Translation workflow with a missing-string check in CIHS
146 hard-coded layout texts, 26 in code, 25 toastsAll user text in resources; lint rule against literalsMS
Settings language change stores a JSON array, so server-side content falls back to EnglishStore the language code; one source of truth for UI and data languageHS
Category stored as a localized label ("Farmer", "రైతు") and parsed backStore codes, render labelsHS
Reference data languages hard-coded per table (product master ENG/TEL only, UOM ENG only)One localized-label table filled from the config bundleMM
172 unused string keysRemove during the portLS

Observability

Problem todayImprovementImpactEffort
Sync health is visible only by zipping device DBsRedacted sync-health telemetry (pending by type, oldest age, failure codes) to the App ServiceHS
Four logging facades, 174 printStackTraceOne facade (Timber or Kermit) with PII redactionMS
No request correlation between app and backendRequest id header echoed and logged end to endMS
Worker tag collision: endorsement and analytics workers share one unique nameOne sync job; distinct names if more are neededMS

Release and config

Problem todayImprovementImpactEffort
Environment switched by commenting XML in build_type_config.xml (108 commits)Build flavors (dev, stage, prod) with per-flavor App Service URLHS
versions.gradle mostly dead but bumped constantlyGradle version catalogLS
jcenter(), mavenLocal() and a stdlib downgrade hackMaven Central and Google only; no forced downgradesMS
Crop Actual gated by listing exact "versionName_versionCode" values in configMinimum-version gate in the App ServiceMS
Hard-coded cut-offs (2026-09-01 for compact blocks, 0.5 acre, 5 acres, excluded product ZBNF_FIELD_BEANS)Move to the config bundleMS
Release-only checks (BuildConfig.DEBUG) make debug behave differentlyFeature flags instead of build-type checksLS

Likely bugs to fix or not carry over

BugWhereEffect
ATM down-sync inserts the unfiltered list instead of the one excluding pending ATMsapp/.../view/option/OptionViewModel.kt (~330), app/.../view/datasetup/DataSetupViewModel.kt (~1085)Pending ATM edits are overwritten
VO geo filter clears the whole ie_flat_entity table before inserting resultsEndorsementRepositoryImpl / VO filter (clearTable())Unsynced farmer rows at risk; list replaced by 10 rows
insertIEList uses REPLACEdata/.../repo/DataRepository.ktDevice-only flags (hasATM, fetch flags, AAP JSON) reset on re-download
hasCompactBlock set by "last PLI wins"data/.../dao/IEPortfolioDao.kt savePortfolioChildEntitiesChip shows wrong state
updatePortfolio sets currentSeason on every PLIPortfolioLineItemDaoGlobal overwrite of season labels
Latest PLI ordered by TEXT appCreatedAtgetLatestPortfolioItemByTitleIdWrong "latest" version possible
ATM harvest sends farmingModelLineItemId = if (id == 0) id else nullapp/.../view/atm/harvest/AtmHarvestActivity.ktReal line-item id is never sent
Harvest photo and GPS are captured but never saved or sentATM harvest and SelectHarvestBottomSheet / HarvestLogsMandatory photo goes nowhere
Continuous harvest of one crop marks all ATM crops PENDING; whole marks all COMPLETEDAtmHarvestViewModel.updateFarmingModelStatusWrong harvest state on other crops
ATM harvest and AAP 2025-26 bypass the sync ledgerAtmHarvestViewModel.kt:47-53, data/.../entity/IEFlatEntity.kt:495-523No dependencies, not visible in Pending Sync
Compact block list "< 5 Acres" total never reset between rowsCompactBlockListAdapter.bindFlag accumulates across cards
Compact block dates shown as raw millisecondsCompact block listUnreadable dates
Compact blocks before 2026-09-01 hidden by a hard-coded dateCompactBlockViewModel.kt:38Older blocks disappear
Default CB name "CB <count>" can collide; uniqueness only checked on renameCompact block createDuplicate names
Settings language change stores the whole ["te","తెలుగు"] arrayapp/.../view/splash/SelectLanguageFragment.kt, LanguageUtil.ktServer content in English while UI is Telugu
"Change VO" in load-more mode may not download the new VODataSetupActivity after SelectWorkNodeActivityOld farmers stay until manual refresh (verify on device)
Synthetic work-node ids (village×100 + index)data/.../setup/SetUpWorknode.ktCollisions with ≥100 habitations or VOs
Instructions sorted by instruction_order as textProgram instructions"10" before "2"
Name search lower-cases the name but not the query; phone search uses !!app/.../view/ie/IeFragment.ktCapitals find nothing; crash on missing phone
Model filter is multi-select but applied as if/else-ifFarmer list filterOnly the first ticked model applies
Photo height uses integer divisionAddIeFragment photo compression0 or 100 px heights, distorted images
Age focus check !b && ageInt < 18 || ageInt > 110AddIeFragmentError shown while still typing
checkLand() reads farmerCategory while the radio writes currentFarmerCategoryAddIeFragmentNewly changed category not seen
Father details stored in spouse columns, meaning decided by marital status at syncfragment_add_ie.xml, IEMapper.ktData meaning flips if marital status changes
Land delete removes the local row before the server DELETE succeedsLand asset deleteLand vanishes locally, stays on server
Leased-in edit saves per parcel and pops on the last callbackAddUpdateAssetFragmentPartial saves on mid-way failure
DKG creates a new record on every submitAddDKGActivitySeveral answers per month
"Mark as resolved" overwrites reviewdBy timestamp with the user nameResolveCommentsFragmentReview time lost
AAP PMDS-years check ignores 2024-25; A Grade warning text says C1 but code checks S2SActionPlanFragment2025To2026Valid input rejected; misleading message
AAP isDataValid overwritten by the last edited fieldActionPlanFragment2025To2026Earlier invalid field can pass
Geofence accepts 1 point, any GPS accuracy, no area or self-intersection checkGeofencingMapActivity.ktUnusable polygons
AuthInterceptor re-logs in on HTTP 400data/.../net/AuthInterceptor.ktValidation errors trigger re-login
Endorsement and analytics workers share one unique-work nameFetchEndorsementDataWorker.kt:84One refresh silently skips the other
DELETE answered "already deactivated" is retried foreverBaseResponseHandler pathEndless retries
onSourceExpire arguments swappedoffline-core SyncQueueProcessor.java:276Dependents expire with wrong data
Queue priority ignored (sorted by create time first)offline-android OperationDaoSqLiteImpl.java:183Priority has no effect
Notifications are never marked readNotificationDao.markNotificationAsRead unusedUnread count only grows
Device conflict "Check Sync" clears the task stackDeviceRegistrationActivityBack exits the app
RoFR tooltip compares a sub-category with a functionality constantAssetMainAdapter.getToolTipStringTooltip never shows