diff --git a/CHANGELOG.md b/CHANGELOG.md
index d9ef7458cf0..72dfadd6724 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,31 @@
# CHANGELOG
+## Next Release
+
+### Internal
+- Bump AGP to v8.12.0
+- Bump Lint to v31.12.0
+- Bump Kotlin to v2.2.0
+- Bump KSP to v2.2.0-2.0.2
+- Bump dagger to v2.57
+- Bump okhttp to v5.1.0
+- Bump retrofit to v3.0.0
+- Bump sqlcipher to v4.10.0
+- Bump AndroidX AppCompat to v1.7.1
+- Bump AndroidX Fragment KTX to v1.8.8
+- Bump asm to v9.8
+- Bump firebase remote config to v23.0.0
+- Bump Firebase Analytics KTX to v22.5.0
+- Bump Jackson Core to v2.19.2
+- Bump Lottie Compose to v6.6.7
+- Add `isUsingSmokelessTobacco` in `MedicalHistory` table
+
+### Changes
+
+- Update min sdk version to 26 (Android 8.0)
+- Update statin translations for `om-ET` and `sid-ET`
+- Update tobacco use dialog to single option select in Ethiopia
+
## 2025.08.13
### Internal
@@ -27,7 +53,6 @@
- Rename `Smoker` to `Smokes`
- Update tobacco translations for `bn-BD`, `ta-LK` and `si-LK`
- Remove `LongTeleconsultMessageBuilder_Old.kt`
-- Update statin translations for `om-ET` and `sid-ET`
## 2025.05.20
@@ -51,6 +76,7 @@
- Bump Sentry to v8.11.0
- Bump Dagger to v2.56.2
- Bump Coroutines to v1.10.2
+- Bump AGP to v8.9.2
- Skip Sentry config during app startup in the debug mode
- Show spanish language option in non-production builds only
- Increase lab based risk threshold to 20%
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index b6727ac24c2..df70ae00e8d 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -184,19 +184,6 @@ android {
beforeVariants { variant ->
variant.enable = variant.name !in filteredVariants
}
-
- onVariants(selector().all()) { variant ->
- afterEvaluate {
- // This is a workaround for https://issuetracker.google.com/301245705 which depends on internal
- // implementations of the android gradle plugin and the ksp gradle plugin which might change in the future
- // in an unpredictable way.
- val variantName = variant.name.replaceFirstChar { it.titlecase() }
- project.tasks.getByName("ksp" + variantName + "Kotlin") {
- val dataBindingTask = project.tasks.getByName("dataBindingGenBaseClasses$variantName") as DataBindingGenBaseClassesTask
- (this as AbstractKotlinCompileTool<*>).setSource(dataBindingTask.sourceOutFolder)
- }
- }
- }
}
lint {
@@ -216,7 +203,10 @@ android {
kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
- freeCompilerArgs = freeCompilerArgs + listOf("-Xopt-in=kotlinx.coroutines.ExperimentalCoroutinesApi")
+ freeCompilerArgs = freeCompilerArgs + listOf(
+ "-Xopt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
+ "-Xannotation-default-target=param-property"
+ )
}
sourceSets {
diff --git a/app/lint.xml b/app/lint.xml
index b9bdc285f58..2119c0d92f9 100644
--- a/app/lint.xml
+++ b/app/lint.xml
@@ -55,6 +55,7 @@
+
diff --git a/app/schemas/org.simple.clinic.AppDatabase/121.json b/app/schemas/org.simple.clinic.AppDatabase/121.json
new file mode 100644
index 00000000000..637ea4a4173
--- /dev/null
+++ b/app/schemas/org.simple.clinic.AppDatabase/121.json
@@ -0,0 +1,2211 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 121,
+ "identityHash": "5d488050f3e10d8e49723f7c5e4a1085",
+ "entities": [
+ {
+ "tableName": "Patient",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `addressUuid` TEXT NOT NULL, `fullName` TEXT NOT NULL, `gender` TEXT NOT NULL, `status` TEXT NOT NULL, `createdAt` TEXT NOT NULL, `updatedAt` TEXT NOT NULL, `deletedAt` TEXT, `recordedAt` TEXT NOT NULL, `syncStatus` TEXT NOT NULL, `reminderConsent` TEXT NOT NULL, `deletedReason` TEXT, `registeredFacilityId` TEXT, `assignedFacilityId` TEXT, `retainUntil` TEXT, `eligibleForReassignment` TEXT NOT NULL, `age_value` INTEGER, `age_updatedAt` TEXT, `dateOfBirth` TEXT, PRIMARY KEY(`uuid`), FOREIGN KEY(`addressUuid`) REFERENCES `PatientAddress`(`uuid`) ON UPDATE CASCADE ON DELETE CASCADE )",
+ "fields": [
+ {
+ "fieldPath": "uuid",
+ "columnName": "uuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "addressUuid",
+ "columnName": "addressUuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "fullName",
+ "columnName": "fullName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "gender",
+ "columnName": "gender",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "status",
+ "columnName": "status",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "createdAt",
+ "columnName": "createdAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "updatedAt",
+ "columnName": "updatedAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deletedAt",
+ "columnName": "deletedAt",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "recordedAt",
+ "columnName": "recordedAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "syncStatus",
+ "columnName": "syncStatus",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "reminderConsent",
+ "columnName": "reminderConsent",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deletedReason",
+ "columnName": "deletedReason",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "registeredFacilityId",
+ "columnName": "registeredFacilityId",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "assignedFacilityId",
+ "columnName": "assignedFacilityId",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "retainUntil",
+ "columnName": "retainUntil",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "eligibleForReassignment",
+ "columnName": "eligibleForReassignment",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "ageDetails.ageValue",
+ "columnName": "age_value",
+ "affinity": "INTEGER"
+ },
+ {
+ "fieldPath": "ageDetails.ageUpdatedAt",
+ "columnName": "age_updatedAt",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "ageDetails.dateOfBirth",
+ "columnName": "dateOfBirth",
+ "affinity": "TEXT"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "uuid"
+ ]
+ },
+ "indices": [
+ {
+ "name": "index_Patient_addressUuid",
+ "unique": false,
+ "columnNames": [
+ "addressUuid"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_Patient_addressUuid` ON `${TABLE_NAME}` (`addressUuid`)"
+ },
+ {
+ "name": "index_Patient_assignedFacilityId",
+ "unique": false,
+ "columnNames": [
+ "assignedFacilityId"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_Patient_assignedFacilityId` ON `${TABLE_NAME}` (`assignedFacilityId`)"
+ }
+ ],
+ "foreignKeys": [
+ {
+ "table": "PatientAddress",
+ "onDelete": "CASCADE",
+ "onUpdate": "CASCADE",
+ "columns": [
+ "addressUuid"
+ ],
+ "referencedColumns": [
+ "uuid"
+ ]
+ }
+ ]
+ },
+ {
+ "tableName": "PatientAddress",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `streetAddress` TEXT, `colonyOrVillage` TEXT, `zone` TEXT, `district` TEXT NOT NULL, `state` TEXT NOT NULL, `country` TEXT, `createdAt` TEXT NOT NULL, `updatedAt` TEXT NOT NULL, `deletedAt` TEXT, PRIMARY KEY(`uuid`))",
+ "fields": [
+ {
+ "fieldPath": "uuid",
+ "columnName": "uuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "streetAddress",
+ "columnName": "streetAddress",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "colonyOrVillage",
+ "columnName": "colonyOrVillage",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "zone",
+ "columnName": "zone",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "district",
+ "columnName": "district",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "state",
+ "columnName": "state",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "country",
+ "columnName": "country",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "createdAt",
+ "columnName": "createdAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "updatedAt",
+ "columnName": "updatedAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deletedAt",
+ "columnName": "deletedAt",
+ "affinity": "TEXT"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "uuid"
+ ]
+ }
+ },
+ {
+ "tableName": "PatientPhoneNumber",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `patientUuid` TEXT NOT NULL, `number` TEXT NOT NULL, `phoneType` TEXT NOT NULL, `active` INTEGER NOT NULL, `createdAt` TEXT NOT NULL, `updatedAt` TEXT NOT NULL, `deletedAt` TEXT, PRIMARY KEY(`uuid`), FOREIGN KEY(`patientUuid`) REFERENCES `Patient`(`uuid`) ON UPDATE CASCADE ON DELETE CASCADE )",
+ "fields": [
+ {
+ "fieldPath": "uuid",
+ "columnName": "uuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "patientUuid",
+ "columnName": "patientUuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "number",
+ "columnName": "number",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "phoneType",
+ "columnName": "phoneType",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "active",
+ "columnName": "active",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "createdAt",
+ "columnName": "createdAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "updatedAt",
+ "columnName": "updatedAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deletedAt",
+ "columnName": "deletedAt",
+ "affinity": "TEXT"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "uuid"
+ ]
+ },
+ "indices": [
+ {
+ "name": "index_PatientPhoneNumber_patientUuid",
+ "unique": false,
+ "columnNames": [
+ "patientUuid"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_PatientPhoneNumber_patientUuid` ON `${TABLE_NAME}` (`patientUuid`)"
+ }
+ ],
+ "foreignKeys": [
+ {
+ "table": "Patient",
+ "onDelete": "CASCADE",
+ "onUpdate": "CASCADE",
+ "columns": [
+ "patientUuid"
+ ],
+ "referencedColumns": [
+ "uuid"
+ ]
+ }
+ ]
+ },
+ {
+ "tableName": "BloodPressureMeasurement",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `syncStatus` TEXT NOT NULL, `userUuid` TEXT NOT NULL, `facilityUuid` TEXT NOT NULL, `patientUuid` TEXT NOT NULL, `createdAt` TEXT NOT NULL, `updatedAt` TEXT NOT NULL, `deletedAt` TEXT, `recordedAt` TEXT NOT NULL, `systolic` INTEGER NOT NULL, `diastolic` INTEGER NOT NULL, PRIMARY KEY(`uuid`))",
+ "fields": [
+ {
+ "fieldPath": "uuid",
+ "columnName": "uuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "syncStatus",
+ "columnName": "syncStatus",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userUuid",
+ "columnName": "userUuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "facilityUuid",
+ "columnName": "facilityUuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "patientUuid",
+ "columnName": "patientUuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "createdAt",
+ "columnName": "createdAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "updatedAt",
+ "columnName": "updatedAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deletedAt",
+ "columnName": "deletedAt",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "recordedAt",
+ "columnName": "recordedAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "reading.systolic",
+ "columnName": "systolic",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "reading.diastolic",
+ "columnName": "diastolic",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "uuid"
+ ]
+ },
+ "indices": [
+ {
+ "name": "index_BloodPressureMeasurement_patientUuid",
+ "unique": false,
+ "columnNames": [
+ "patientUuid"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_BloodPressureMeasurement_patientUuid` ON `${TABLE_NAME}` (`patientUuid`)"
+ },
+ {
+ "name": "index_BloodPressureMeasurement_facilityUuid",
+ "unique": false,
+ "columnNames": [
+ "facilityUuid"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_BloodPressureMeasurement_facilityUuid` ON `${TABLE_NAME}` (`facilityUuid`)"
+ }
+ ]
+ },
+ {
+ "tableName": "PrescribedDrug",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `name` TEXT NOT NULL, `dosage` TEXT, `rxNormCode` TEXT, `isDeleted` INTEGER NOT NULL, `isProtocolDrug` INTEGER NOT NULL, `patientUuid` TEXT NOT NULL, `facilityUuid` TEXT NOT NULL, `syncStatus` TEXT NOT NULL, `frequency` TEXT, `durationInDays` INTEGER, `teleconsultationId` TEXT, `createdAt` TEXT NOT NULL, `updatedAt` TEXT NOT NULL, `deletedAt` TEXT, PRIMARY KEY(`uuid`))",
+ "fields": [
+ {
+ "fieldPath": "uuid",
+ "columnName": "uuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "dosage",
+ "columnName": "dosage",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "rxNormCode",
+ "columnName": "rxNormCode",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "isDeleted",
+ "columnName": "isDeleted",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isProtocolDrug",
+ "columnName": "isProtocolDrug",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "patientUuid",
+ "columnName": "patientUuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "facilityUuid",
+ "columnName": "facilityUuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "syncStatus",
+ "columnName": "syncStatus",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "frequency",
+ "columnName": "frequency",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "durationInDays",
+ "columnName": "durationInDays",
+ "affinity": "INTEGER"
+ },
+ {
+ "fieldPath": "teleconsultationId",
+ "columnName": "teleconsultationId",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "timestamps.createdAt",
+ "columnName": "createdAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamps.updatedAt",
+ "columnName": "updatedAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamps.deletedAt",
+ "columnName": "deletedAt",
+ "affinity": "TEXT"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "uuid"
+ ]
+ },
+ "indices": [
+ {
+ "name": "index_PrescribedDrug_patientUuid",
+ "unique": false,
+ "columnNames": [
+ "patientUuid"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_PrescribedDrug_patientUuid` ON `${TABLE_NAME}` (`patientUuid`)"
+ },
+ {
+ "name": "index_PrescribedDrug_facilityUuid",
+ "unique": false,
+ "columnNames": [
+ "facilityUuid"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_PrescribedDrug_facilityUuid` ON `${TABLE_NAME}` (`facilityUuid`)"
+ }
+ ]
+ },
+ {
+ "tableName": "Facility",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `name` TEXT NOT NULL, `facilityType` TEXT, `streetAddress` TEXT, `villageOrColony` TEXT, `district` TEXT NOT NULL, `state` TEXT NOT NULL, `country` TEXT NOT NULL, `pinCode` TEXT, `protocolUuid` TEXT, `groupUuid` TEXT, `createdAt` TEXT NOT NULL, `updatedAt` TEXT NOT NULL, `syncStatus` TEXT NOT NULL, `deletedAt` TEXT, `syncGroup` TEXT NOT NULL, `location_latitude` REAL, `location_longitude` REAL, `config_diabetesManagementEnabled` INTEGER NOT NULL, `config_teleconsultationEnabled` INTEGER, `config_monthlyScreeningReportsEnabled` INTEGER, `config_monthlySuppliesReportsEnabled` INTEGER NOT NULL, PRIMARY KEY(`uuid`))",
+ "fields": [
+ {
+ "fieldPath": "uuid",
+ "columnName": "uuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "facilityType",
+ "columnName": "facilityType",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "streetAddress",
+ "columnName": "streetAddress",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "villageOrColony",
+ "columnName": "villageOrColony",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "district",
+ "columnName": "district",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "state",
+ "columnName": "state",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "country",
+ "columnName": "country",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "pinCode",
+ "columnName": "pinCode",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "protocolUuid",
+ "columnName": "protocolUuid",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "groupUuid",
+ "columnName": "groupUuid",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "createdAt",
+ "columnName": "createdAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "updatedAt",
+ "columnName": "updatedAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "syncStatus",
+ "columnName": "syncStatus",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deletedAt",
+ "columnName": "deletedAt",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "syncGroup",
+ "columnName": "syncGroup",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "location.latitude",
+ "columnName": "location_latitude",
+ "affinity": "REAL"
+ },
+ {
+ "fieldPath": "location.longitude",
+ "columnName": "location_longitude",
+ "affinity": "REAL"
+ },
+ {
+ "fieldPath": "config.diabetesManagementEnabled",
+ "columnName": "config_diabetesManagementEnabled",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "config.teleconsultationEnabled",
+ "columnName": "config_teleconsultationEnabled",
+ "affinity": "INTEGER"
+ },
+ {
+ "fieldPath": "config.monthlyScreeningReportsEnabled",
+ "columnName": "config_monthlyScreeningReportsEnabled",
+ "affinity": "INTEGER"
+ },
+ {
+ "fieldPath": "config.monthlySuppliesReportsEnabled",
+ "columnName": "config_monthlySuppliesReportsEnabled",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "uuid"
+ ]
+ }
+ },
+ {
+ "tableName": "LoggedInUser",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `fullName` TEXT NOT NULL, `phoneNumber` TEXT NOT NULL, `pinDigest` TEXT NOT NULL, `status` TEXT NOT NULL, `createdAt` TEXT NOT NULL, `updatedAt` TEXT NOT NULL, `loggedInStatus` TEXT NOT NULL, `registrationFacilityUuid` TEXT NOT NULL, `currentFacilityUuid` TEXT NOT NULL, `teleconsultPhoneNumber` TEXT, `capability_canTeleconsult` TEXT, PRIMARY KEY(`uuid`))",
+ "fields": [
+ {
+ "fieldPath": "uuid",
+ "columnName": "uuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "fullName",
+ "columnName": "fullName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "phoneNumber",
+ "columnName": "phoneNumber",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "pinDigest",
+ "columnName": "pinDigest",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "status",
+ "columnName": "status",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "createdAt",
+ "columnName": "createdAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "updatedAt",
+ "columnName": "updatedAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loggedInStatus",
+ "columnName": "loggedInStatus",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "registrationFacilityUuid",
+ "columnName": "registrationFacilityUuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "currentFacilityUuid",
+ "columnName": "currentFacilityUuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teleconsultPhoneNumber",
+ "columnName": "teleconsultPhoneNumber",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "capabilities.canTeleconsult",
+ "columnName": "capability_canTeleconsult",
+ "affinity": "TEXT"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "uuid"
+ ]
+ }
+ },
+ {
+ "tableName": "Appointment",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `patientUuid` TEXT NOT NULL, `facilityUuid` TEXT NOT NULL, `scheduledDate` TEXT NOT NULL, `status` TEXT NOT NULL, `cancelReason` TEXT, `remindOn` TEXT, `agreedToVisit` INTEGER, `appointmentType` TEXT NOT NULL, `syncStatus` TEXT NOT NULL, `createdAt` TEXT NOT NULL, `updatedAt` TEXT NOT NULL, `deletedAt` TEXT, `creationFacilityUuid` TEXT, PRIMARY KEY(`uuid`))",
+ "fields": [
+ {
+ "fieldPath": "uuid",
+ "columnName": "uuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "patientUuid",
+ "columnName": "patientUuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "facilityUuid",
+ "columnName": "facilityUuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "scheduledDate",
+ "columnName": "scheduledDate",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "status",
+ "columnName": "status",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "cancelReason",
+ "columnName": "cancelReason",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "remindOn",
+ "columnName": "remindOn",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "agreedToVisit",
+ "columnName": "agreedToVisit",
+ "affinity": "INTEGER"
+ },
+ {
+ "fieldPath": "appointmentType",
+ "columnName": "appointmentType",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "syncStatus",
+ "columnName": "syncStatus",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "createdAt",
+ "columnName": "createdAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "updatedAt",
+ "columnName": "updatedAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deletedAt",
+ "columnName": "deletedAt",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "creationFacilityUuid",
+ "columnName": "creationFacilityUuid",
+ "affinity": "TEXT"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "uuid"
+ ]
+ },
+ "indices": [
+ {
+ "name": "index_Appointment_patientUuid",
+ "unique": false,
+ "columnNames": [
+ "patientUuid"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_Appointment_patientUuid` ON `${TABLE_NAME}` (`patientUuid`)"
+ },
+ {
+ "name": "index_Appointment_creationFacilityUuid",
+ "unique": false,
+ "columnNames": [
+ "creationFacilityUuid"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_Appointment_creationFacilityUuid` ON `${TABLE_NAME}` (`creationFacilityUuid`)"
+ },
+ {
+ "name": "index_Appointment_facilityUuid",
+ "unique": false,
+ "columnNames": [
+ "facilityUuid"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_Appointment_facilityUuid` ON `${TABLE_NAME}` (`facilityUuid`)"
+ }
+ ]
+ },
+ {
+ "tableName": "MedicalHistory",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `patientUuid` TEXT NOT NULL, `diagnosedWithHypertension` TEXT NOT NULL, `isOnHypertensionTreatment` TEXT NOT NULL, `isOnDiabetesTreatment` TEXT NOT NULL, `hasHadHeartAttack` TEXT NOT NULL, `hasHadStroke` TEXT NOT NULL, `hasHadKidneyDisease` TEXT NOT NULL, `hasDiabetes` TEXT NOT NULL, `isSmoking` TEXT NOT NULL, `isUsingSmokelessTobacco` TEXT NOT NULL, `cholesterol_value` REAL, `syncStatus` TEXT NOT NULL, `createdAt` TEXT NOT NULL, `updatedAt` TEXT NOT NULL, `deletedAt` TEXT, PRIMARY KEY(`uuid`))",
+ "fields": [
+ {
+ "fieldPath": "uuid",
+ "columnName": "uuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "patientUuid",
+ "columnName": "patientUuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diagnosedWithHypertension",
+ "columnName": "diagnosedWithHypertension",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isOnHypertensionTreatment",
+ "columnName": "isOnHypertensionTreatment",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isOnDiabetesTreatment",
+ "columnName": "isOnDiabetesTreatment",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "hasHadHeartAttack",
+ "columnName": "hasHadHeartAttack",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "hasHadStroke",
+ "columnName": "hasHadStroke",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "hasHadKidneyDisease",
+ "columnName": "hasHadKidneyDisease",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diagnosedWithDiabetes",
+ "columnName": "hasDiabetes",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isSmoking",
+ "columnName": "isSmoking",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isUsingSmokelessTobacco",
+ "columnName": "isUsingSmokelessTobacco",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "cholesterol",
+ "columnName": "cholesterol_value",
+ "affinity": "REAL"
+ },
+ {
+ "fieldPath": "syncStatus",
+ "columnName": "syncStatus",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "createdAt",
+ "columnName": "createdAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "updatedAt",
+ "columnName": "updatedAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deletedAt",
+ "columnName": "deletedAt",
+ "affinity": "TEXT"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "uuid"
+ ]
+ },
+ "indices": [
+ {
+ "name": "index_MedicalHistory_patientUuid",
+ "unique": false,
+ "columnNames": [
+ "patientUuid"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_MedicalHistory_patientUuid` ON `${TABLE_NAME}` (`patientUuid`)"
+ }
+ ]
+ },
+ {
+ "tableName": "OngoingLoginEntry",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `phoneNumber` TEXT, `pin` TEXT, `fullName` TEXT, `pinDigest` TEXT, `registrationFacilityUuid` TEXT, `status` TEXT, `createdAt` TEXT, `updatedAt` TEXT, `teleconsultPhoneNumber` TEXT, `capability_canTeleconsult` TEXT, PRIMARY KEY(`uuid`))",
+ "fields": [
+ {
+ "fieldPath": "uuid",
+ "columnName": "uuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "phoneNumber",
+ "columnName": "phoneNumber",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "pin",
+ "columnName": "pin",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "fullName",
+ "columnName": "fullName",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "pinDigest",
+ "columnName": "pinDigest",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "registrationFacilityUuid",
+ "columnName": "registrationFacilityUuid",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "status",
+ "columnName": "status",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "createdAt",
+ "columnName": "createdAt",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "updatedAt",
+ "columnName": "updatedAt",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "teleconsultPhoneNumber",
+ "columnName": "teleconsultPhoneNumber",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "capabilities.canTeleconsult",
+ "columnName": "capability_canTeleconsult",
+ "affinity": "TEXT"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "uuid"
+ ]
+ }
+ },
+ {
+ "tableName": "Protocol",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `name` TEXT NOT NULL, `followUpDays` INTEGER NOT NULL, `createdAt` TEXT NOT NULL, `updatedAt` TEXT NOT NULL, `syncStatus` TEXT NOT NULL, `deletedAt` TEXT, PRIMARY KEY(`uuid`))",
+ "fields": [
+ {
+ "fieldPath": "uuid",
+ "columnName": "uuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "followUpDays",
+ "columnName": "followUpDays",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "createdAt",
+ "columnName": "createdAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "updatedAt",
+ "columnName": "updatedAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "syncStatus",
+ "columnName": "syncStatus",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deletedAt",
+ "columnName": "deletedAt",
+ "affinity": "TEXT"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "uuid"
+ ]
+ }
+ },
+ {
+ "tableName": "ProtocolDrug",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `protocolUuid` TEXT NOT NULL, `name` TEXT NOT NULL, `rxNormCode` TEXT, `dosage` TEXT NOT NULL, `createdAt` TEXT NOT NULL, `updatedAt` TEXT NOT NULL, `deletedAt` TEXT, `order` INTEGER NOT NULL, PRIMARY KEY(`uuid`), FOREIGN KEY(`protocolUuid`) REFERENCES `Protocol`(`uuid`) ON UPDATE NO ACTION ON DELETE CASCADE )",
+ "fields": [
+ {
+ "fieldPath": "uuid",
+ "columnName": "uuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "protocolUuid",
+ "columnName": "protocolUuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "rxNormCode",
+ "columnName": "rxNormCode",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "dosage",
+ "columnName": "dosage",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "createdAt",
+ "columnName": "createdAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "updatedAt",
+ "columnName": "updatedAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deletedAt",
+ "columnName": "deletedAt",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "order",
+ "columnName": "order",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "uuid"
+ ]
+ },
+ "indices": [
+ {
+ "name": "index_ProtocolDrug_protocolUuid",
+ "unique": false,
+ "columnNames": [
+ "protocolUuid"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_ProtocolDrug_protocolUuid` ON `${TABLE_NAME}` (`protocolUuid`)"
+ }
+ ],
+ "foreignKeys": [
+ {
+ "table": "Protocol",
+ "onDelete": "CASCADE",
+ "onUpdate": "NO ACTION",
+ "columns": [
+ "protocolUuid"
+ ],
+ "referencedColumns": [
+ "uuid"
+ ]
+ }
+ ]
+ },
+ {
+ "tableName": "BusinessId",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `patientUuid` TEXT NOT NULL, `metaVersion` TEXT NOT NULL, `meta` TEXT NOT NULL, `createdAt` TEXT NOT NULL, `updatedAt` TEXT NOT NULL, `deletedAt` TEXT, `searchHelp` TEXT NOT NULL, `identifier` TEXT NOT NULL, `identifierType` TEXT NOT NULL, PRIMARY KEY(`uuid`), FOREIGN KEY(`patientUuid`) REFERENCES `Patient`(`uuid`) ON UPDATE NO ACTION ON DELETE CASCADE )",
+ "fields": [
+ {
+ "fieldPath": "uuid",
+ "columnName": "uuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "patientUuid",
+ "columnName": "patientUuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "metaDataVersion",
+ "columnName": "metaVersion",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "metaData",
+ "columnName": "meta",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "createdAt",
+ "columnName": "createdAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "updatedAt",
+ "columnName": "updatedAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deletedAt",
+ "columnName": "deletedAt",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "searchHelp",
+ "columnName": "searchHelp",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "identifier.value",
+ "columnName": "identifier",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "identifier.type",
+ "columnName": "identifierType",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "uuid"
+ ]
+ },
+ "indices": [
+ {
+ "name": "index_BusinessId_patientUuid",
+ "unique": false,
+ "columnNames": [
+ "patientUuid"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_BusinessId_patientUuid` ON `${TABLE_NAME}` (`patientUuid`)"
+ },
+ {
+ "name": "index_BusinessId_identifier",
+ "unique": false,
+ "columnNames": [
+ "identifier"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_BusinessId_identifier` ON `${TABLE_NAME}` (`identifier`)"
+ }
+ ],
+ "foreignKeys": [
+ {
+ "table": "Patient",
+ "onDelete": "CASCADE",
+ "onUpdate": "NO ACTION",
+ "columns": [
+ "patientUuid"
+ ],
+ "referencedColumns": [
+ "uuid"
+ ]
+ }
+ ]
+ },
+ {
+ "tableName": "MissingPhoneReminder",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`patientUuid` TEXT NOT NULL, `remindedAt` TEXT NOT NULL, PRIMARY KEY(`patientUuid`))",
+ "fields": [
+ {
+ "fieldPath": "patientUuid",
+ "columnName": "patientUuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "remindedAt",
+ "columnName": "remindedAt",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "patientUuid"
+ ]
+ }
+ },
+ {
+ "tableName": "BloodSugarMeasurements",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `recordedAt` TEXT NOT NULL, `patientUuid` TEXT NOT NULL, `userUuid` TEXT NOT NULL, `facilityUuid` TEXT NOT NULL, `syncStatus` TEXT NOT NULL, `reading_value` TEXT NOT NULL, `reading_type` TEXT NOT NULL, `createdAt` TEXT NOT NULL, `updatedAt` TEXT NOT NULL, `deletedAt` TEXT, PRIMARY KEY(`uuid`))",
+ "fields": [
+ {
+ "fieldPath": "uuid",
+ "columnName": "uuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "recordedAt",
+ "columnName": "recordedAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "patientUuid",
+ "columnName": "patientUuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userUuid",
+ "columnName": "userUuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "facilityUuid",
+ "columnName": "facilityUuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "syncStatus",
+ "columnName": "syncStatus",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "reading.value",
+ "columnName": "reading_value",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "reading.type",
+ "columnName": "reading_type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamps.createdAt",
+ "columnName": "createdAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamps.updatedAt",
+ "columnName": "updatedAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamps.deletedAt",
+ "columnName": "deletedAt",
+ "affinity": "TEXT"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "uuid"
+ ]
+ },
+ "indices": [
+ {
+ "name": "index_BloodSugarMeasurements_patientUuid",
+ "unique": false,
+ "columnNames": [
+ "patientUuid"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_BloodSugarMeasurements_patientUuid` ON `${TABLE_NAME}` (`patientUuid`)"
+ },
+ {
+ "name": "index_BloodSugarMeasurements_facilityUuid",
+ "unique": false,
+ "columnNames": [
+ "facilityUuid"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_BloodSugarMeasurements_facilityUuid` ON `${TABLE_NAME}` (`facilityUuid`)"
+ }
+ ]
+ },
+ {
+ "tableName": "TextRecords",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `text` TEXT, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "text",
+ "columnName": "text",
+ "affinity": "TEXT"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ }
+ },
+ {
+ "tableName": "TeleconsultationFacilityInfo",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`teleconsultationFacilityId` TEXT NOT NULL, `facilityId` TEXT NOT NULL, `createdAt` TEXT NOT NULL, `updatedAt` TEXT NOT NULL, `deletedAt` TEXT, `syncStatus` TEXT NOT NULL, PRIMARY KEY(`teleconsultationFacilityId`))",
+ "fields": [
+ {
+ "fieldPath": "teleconsultationFacilityId",
+ "columnName": "teleconsultationFacilityId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "facilityId",
+ "columnName": "facilityId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "createdAt",
+ "columnName": "createdAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "updatedAt",
+ "columnName": "updatedAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deletedAt",
+ "columnName": "deletedAt",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "syncStatus",
+ "columnName": "syncStatus",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "teleconsultationFacilityId"
+ ]
+ }
+ },
+ {
+ "tableName": "MedicalOfficer",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`medicalOfficerId` TEXT NOT NULL, `fullName` TEXT NOT NULL, `phoneNumber` TEXT NOT NULL, PRIMARY KEY(`medicalOfficerId`))",
+ "fields": [
+ {
+ "fieldPath": "medicalOfficerId",
+ "columnName": "medicalOfficerId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "fullName",
+ "columnName": "fullName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "phoneNumber",
+ "columnName": "phoneNumber",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "medicalOfficerId"
+ ]
+ }
+ },
+ {
+ "tableName": "TeleconsultationFacilityMedicalOfficersCrossRef",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`teleconsultationFacilityId` TEXT NOT NULL, `medicalOfficerId` TEXT NOT NULL, PRIMARY KEY(`teleconsultationFacilityId`, `medicalOfficerId`))",
+ "fields": [
+ {
+ "fieldPath": "teleconsultationFacilityId",
+ "columnName": "teleconsultationFacilityId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "medicalOfficerId",
+ "columnName": "medicalOfficerId",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "teleconsultationFacilityId",
+ "medicalOfficerId"
+ ]
+ }
+ },
+ {
+ "tableName": "TeleconsultRecord",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `patientId` TEXT NOT NULL, `medicalOfficerId` TEXT NOT NULL, `syncStatus` TEXT NOT NULL, `request_requesterId` TEXT, `request_facilityId` TEXT, `request_requestedAt` TEXT, `request_requesterCompletionStatus` TEXT, `record_recordedAt` TEXT, `record_teleconsultationType` TEXT, `record_patientTookMedicines` TEXT, `record_patientConsented` TEXT, `record_medicalOfficerNumber` TEXT, `createdAt` TEXT NOT NULL, `updatedAt` TEXT NOT NULL, `deletedAt` TEXT, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "patientId",
+ "columnName": "patientId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "medicalOfficerId",
+ "columnName": "medicalOfficerId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "syncStatus",
+ "columnName": "syncStatus",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teleconsultRequestInfo.requesterId",
+ "columnName": "request_requesterId",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "teleconsultRequestInfo.facilityId",
+ "columnName": "request_facilityId",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "teleconsultRequestInfo.requestedAt",
+ "columnName": "request_requestedAt",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "teleconsultRequestInfo.requesterCompletionStatus",
+ "columnName": "request_requesterCompletionStatus",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "teleconsultRecordInfo.recordedAt",
+ "columnName": "record_recordedAt",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "teleconsultRecordInfo.teleconsultationType",
+ "columnName": "record_teleconsultationType",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "teleconsultRecordInfo.patientTookMedicines",
+ "columnName": "record_patientTookMedicines",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "teleconsultRecordInfo.patientConsented",
+ "columnName": "record_patientConsented",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "teleconsultRecordInfo.medicalOfficerNumber",
+ "columnName": "record_medicalOfficerNumber",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "timestamp.createdAt",
+ "columnName": "createdAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamp.updatedAt",
+ "columnName": "updatedAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamp.deletedAt",
+ "columnName": "deletedAt",
+ "affinity": "TEXT"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ }
+ },
+ {
+ "tableName": "Drug",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `category` TEXT, `frequency` TEXT, `composition` TEXT, `dosage` TEXT, `rxNormCode` TEXT, `protocol` TEXT NOT NULL, `common` TEXT NOT NULL, `createdAt` TEXT NOT NULL, `updatedAt` TEXT NOT NULL, `deletedAt` TEXT, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "category",
+ "columnName": "category",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "frequency",
+ "columnName": "frequency",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "composition",
+ "columnName": "composition",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "dosage",
+ "columnName": "dosage",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "rxNormCode",
+ "columnName": "rxNormCode",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "protocol",
+ "columnName": "protocol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "common",
+ "columnName": "common",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamps.createdAt",
+ "columnName": "createdAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamps.updatedAt",
+ "columnName": "updatedAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamps.deletedAt",
+ "columnName": "deletedAt",
+ "affinity": "TEXT"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ }
+ },
+ {
+ "tableName": "CallResult",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `userId` TEXT NOT NULL, `patientId` TEXT, `facilityId` TEXT, `appointmentId` TEXT NOT NULL, `removeReason` TEXT, `outcome` TEXT NOT NULL, `syncStatus` TEXT NOT NULL, `createdAt` TEXT NOT NULL, `updatedAt` TEXT NOT NULL, `deletedAt` TEXT, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userId",
+ "columnName": "userId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "patientId",
+ "columnName": "patientId",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "facilityId",
+ "columnName": "facilityId",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "appointmentId",
+ "columnName": "appointmentId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "removeReason",
+ "columnName": "removeReason",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "outcome",
+ "columnName": "outcome",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "syncStatus",
+ "columnName": "syncStatus",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamps.createdAt",
+ "columnName": "createdAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamps.updatedAt",
+ "columnName": "updatedAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamps.deletedAt",
+ "columnName": "deletedAt",
+ "affinity": "TEXT"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [
+ {
+ "name": "index_CallResult_appointmentId",
+ "unique": false,
+ "columnNames": [
+ "appointmentId"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_CallResult_appointmentId` ON `${TABLE_NAME}` (`appointmentId`)"
+ }
+ ]
+ },
+ {
+ "tableName": "PatientFts",
+ "createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS4(`uuid` TEXT NOT NULL, `fullName` TEXT NOT NULL, content=`Patient`)",
+ "fields": [
+ {
+ "fieldPath": "uuid",
+ "columnName": "uuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "fullName",
+ "columnName": "fullName",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": []
+ },
+ "ftsVersion": "FTS4",
+ "ftsOptions": {
+ "tokenizer": "simple",
+ "tokenizerArgs": [],
+ "contentTable": "Patient",
+ "languageIdColumnName": "",
+ "matchInfo": "FTS4",
+ "notIndexedColumns": [],
+ "prefixSizes": [],
+ "preferredOrder": "ASC"
+ },
+ "contentSyncTriggers": [
+ "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_PatientFts_BEFORE_UPDATE BEFORE UPDATE ON `Patient` BEGIN DELETE FROM `PatientFts` WHERE `docid`=OLD.`rowid`; END",
+ "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_PatientFts_BEFORE_DELETE BEFORE DELETE ON `Patient` BEGIN DELETE FROM `PatientFts` WHERE `docid`=OLD.`rowid`; END",
+ "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_PatientFts_AFTER_UPDATE AFTER UPDATE ON `Patient` BEGIN INSERT INTO `PatientFts`(`docid`, `uuid`, `fullName`) VALUES (NEW.`rowid`, NEW.`uuid`, NEW.`fullName`); END",
+ "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_PatientFts_AFTER_INSERT AFTER INSERT ON `Patient` BEGIN INSERT INTO `PatientFts`(`docid`, `uuid`, `fullName`) VALUES (NEW.`rowid`, NEW.`uuid`, NEW.`fullName`); END"
+ ]
+ },
+ {
+ "tableName": "PatientPhoneNumberFts",
+ "createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS4(`patientUuid` TEXT NOT NULL, `number` TEXT NOT NULL, content=`PatientPhoneNumber`)",
+ "fields": [
+ {
+ "fieldPath": "patientUuid",
+ "columnName": "patientUuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "number",
+ "columnName": "number",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": []
+ },
+ "ftsVersion": "FTS4",
+ "ftsOptions": {
+ "tokenizer": "simple",
+ "tokenizerArgs": [],
+ "contentTable": "PatientPhoneNumber",
+ "languageIdColumnName": "",
+ "matchInfo": "FTS4",
+ "notIndexedColumns": [],
+ "prefixSizes": [],
+ "preferredOrder": "ASC"
+ },
+ "contentSyncTriggers": [
+ "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_PatientPhoneNumberFts_BEFORE_UPDATE BEFORE UPDATE ON `PatientPhoneNumber` BEGIN DELETE FROM `PatientPhoneNumberFts` WHERE `docid`=OLD.`rowid`; END",
+ "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_PatientPhoneNumberFts_BEFORE_DELETE BEFORE DELETE ON `PatientPhoneNumber` BEGIN DELETE FROM `PatientPhoneNumberFts` WHERE `docid`=OLD.`rowid`; END",
+ "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_PatientPhoneNumberFts_AFTER_UPDATE AFTER UPDATE ON `PatientPhoneNumber` BEGIN INSERT INTO `PatientPhoneNumberFts`(`docid`, `patientUuid`, `number`) VALUES (NEW.`rowid`, NEW.`patientUuid`, NEW.`number`); END",
+ "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_PatientPhoneNumberFts_AFTER_INSERT AFTER INSERT ON `PatientPhoneNumber` BEGIN INSERT INTO `PatientPhoneNumberFts`(`docid`, `patientUuid`, `number`) VALUES (NEW.`rowid`, NEW.`patientUuid`, NEW.`number`); END"
+ ]
+ },
+ {
+ "tableName": "BusinessIdFts",
+ "createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS4(`patientUuid` TEXT NOT NULL, `searchHelp` TEXT NOT NULL, content=`BusinessId`)",
+ "fields": [
+ {
+ "fieldPath": "patientUuid",
+ "columnName": "patientUuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "searchHelp",
+ "columnName": "searchHelp",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": []
+ },
+ "ftsVersion": "FTS4",
+ "ftsOptions": {
+ "tokenizer": "simple",
+ "tokenizerArgs": [],
+ "contentTable": "BusinessId",
+ "languageIdColumnName": "",
+ "matchInfo": "FTS4",
+ "notIndexedColumns": [],
+ "prefixSizes": [],
+ "preferredOrder": "ASC"
+ },
+ "contentSyncTriggers": [
+ "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_BusinessIdFts_BEFORE_UPDATE BEFORE UPDATE ON `BusinessId` BEGIN DELETE FROM `BusinessIdFts` WHERE `docid`=OLD.`rowid`; END",
+ "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_BusinessIdFts_BEFORE_DELETE BEFORE DELETE ON `BusinessId` BEGIN DELETE FROM `BusinessIdFts` WHERE `docid`=OLD.`rowid`; END",
+ "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_BusinessIdFts_AFTER_UPDATE AFTER UPDATE ON `BusinessId` BEGIN INSERT INTO `BusinessIdFts`(`docid`, `patientUuid`, `searchHelp`) VALUES (NEW.`rowid`, NEW.`patientUuid`, NEW.`searchHelp`); END",
+ "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_BusinessIdFts_AFTER_INSERT AFTER INSERT ON `BusinessId` BEGIN INSERT INTO `BusinessIdFts`(`docid`, `patientUuid`, `searchHelp`) VALUES (NEW.`rowid`, NEW.`patientUuid`, NEW.`searchHelp`); END"
+ ]
+ },
+ {
+ "tableName": "PatientAddressFts",
+ "createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS4(`uuid` TEXT NOT NULL, `colonyOrVillage` TEXT, content=`PatientAddress`)",
+ "fields": [
+ {
+ "fieldPath": "uuid",
+ "columnName": "uuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "colonyOrVillage",
+ "columnName": "colonyOrVillage",
+ "affinity": "TEXT"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": []
+ },
+ "ftsVersion": "FTS4",
+ "ftsOptions": {
+ "tokenizer": "simple",
+ "tokenizerArgs": [],
+ "contentTable": "PatientAddress",
+ "languageIdColumnName": "",
+ "matchInfo": "FTS4",
+ "notIndexedColumns": [],
+ "prefixSizes": [],
+ "preferredOrder": "ASC"
+ },
+ "contentSyncTriggers": [
+ "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_PatientAddressFts_BEFORE_UPDATE BEFORE UPDATE ON `PatientAddress` BEGIN DELETE FROM `PatientAddressFts` WHERE `docid`=OLD.`rowid`; END",
+ "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_PatientAddressFts_BEFORE_DELETE BEFORE DELETE ON `PatientAddress` BEGIN DELETE FROM `PatientAddressFts` WHERE `docid`=OLD.`rowid`; END",
+ "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_PatientAddressFts_AFTER_UPDATE AFTER UPDATE ON `PatientAddress` BEGIN INSERT INTO `PatientAddressFts`(`docid`, `uuid`, `colonyOrVillage`) VALUES (NEW.`rowid`, NEW.`uuid`, NEW.`colonyOrVillage`); END",
+ "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_PatientAddressFts_AFTER_INSERT AFTER INSERT ON `PatientAddress` BEGIN INSERT INTO `PatientAddressFts`(`docid`, `uuid`, `colonyOrVillage`) VALUES (NEW.`rowid`, NEW.`uuid`, NEW.`colonyOrVillage`); END"
+ ]
+ },
+ {
+ "tableName": "Questionnaire",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `questionnaire_type` TEXT NOT NULL, `layout` TEXT NOT NULL, `deletedAt` TEXT, PRIMARY KEY(`questionnaire_type`))",
+ "fields": [
+ {
+ "fieldPath": "uuid",
+ "columnName": "uuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "questionnaire_type",
+ "columnName": "questionnaire_type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "layout",
+ "columnName": "layout",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deletedAt",
+ "columnName": "deletedAt",
+ "affinity": "TEXT"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "questionnaire_type"
+ ]
+ }
+ },
+ {
+ "tableName": "QuestionnaireResponse",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `questionnaireId` TEXT NOT NULL, `questionnaireType` TEXT NOT NULL, `facilityId` TEXT NOT NULL, `lastUpdatedByUserId` TEXT, `content` TEXT NOT NULL, `syncStatus` TEXT NOT NULL, `createdAt` TEXT NOT NULL, `updatedAt` TEXT NOT NULL, `deletedAt` TEXT, PRIMARY KEY(`uuid`))",
+ "fields": [
+ {
+ "fieldPath": "uuid",
+ "columnName": "uuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "questionnaireId",
+ "columnName": "questionnaireId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "questionnaireType",
+ "columnName": "questionnaireType",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "facilityId",
+ "columnName": "facilityId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastUpdatedByUserId",
+ "columnName": "lastUpdatedByUserId",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "syncStatus",
+ "columnName": "syncStatus",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamps.createdAt",
+ "columnName": "createdAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamps.updatedAt",
+ "columnName": "updatedAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamps.deletedAt",
+ "columnName": "deletedAt",
+ "affinity": "TEXT"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "uuid"
+ ]
+ }
+ },
+ {
+ "tableName": "PatientAttribute",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `patientUuid` TEXT NOT NULL, `userUuid` TEXT NOT NULL, `syncStatus` TEXT NOT NULL, `height` REAL NOT NULL, `weight` REAL NOT NULL, `createdAt` TEXT NOT NULL, `updatedAt` TEXT NOT NULL, `deletedAt` TEXT, PRIMARY KEY(`uuid`))",
+ "fields": [
+ {
+ "fieldPath": "uuid",
+ "columnName": "uuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "patientUuid",
+ "columnName": "patientUuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userUuid",
+ "columnName": "userUuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "syncStatus",
+ "columnName": "syncStatus",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "bmiReading.height",
+ "columnName": "height",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "bmiReading.weight",
+ "columnName": "weight",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamps.createdAt",
+ "columnName": "createdAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamps.updatedAt",
+ "columnName": "updatedAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamps.deletedAt",
+ "columnName": "deletedAt",
+ "affinity": "TEXT"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "uuid"
+ ]
+ },
+ "indices": [
+ {
+ "name": "index_PatientAttribute_patientUuid",
+ "unique": false,
+ "columnNames": [
+ "patientUuid"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_PatientAttribute_patientUuid` ON `${TABLE_NAME}` (`patientUuid`)"
+ }
+ ]
+ },
+ {
+ "tableName": "CVDRisk",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `patientUuid` TEXT NOT NULL, `syncStatus` TEXT NOT NULL, `min` INTEGER NOT NULL, `max` INTEGER NOT NULL, `createdAt` TEXT NOT NULL, `updatedAt` TEXT NOT NULL, `deletedAt` TEXT, PRIMARY KEY(`uuid`))",
+ "fields": [
+ {
+ "fieldPath": "uuid",
+ "columnName": "uuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "patientUuid",
+ "columnName": "patientUuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "syncStatus",
+ "columnName": "syncStatus",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "riskScore.min",
+ "columnName": "min",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "riskScore.max",
+ "columnName": "max",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamps.createdAt",
+ "columnName": "createdAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamps.updatedAt",
+ "columnName": "updatedAt",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamps.deletedAt",
+ "columnName": "deletedAt",
+ "affinity": "TEXT"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "uuid"
+ ]
+ },
+ "indices": [
+ {
+ "name": "index_CVDRisk_patientUuid",
+ "unique": false,
+ "columnNames": [
+ "patientUuid"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_CVDRisk_patientUuid` ON `${TABLE_NAME}` (`patientUuid`)"
+ }
+ ]
+ }
+ ],
+ "views": [
+ {
+ "viewName": "PatientSearchResult",
+ "createSql": "CREATE VIEW `${VIEW_NAME}` AS SELECT P.uuid, P.fullName, P.gender, P.dateOfBirth, P.age_value, P.age_updatedAt,\n P.assignedFacilityId, P.status, P.eligibleForReassignment,\n PA.uuid addr_uuid, PA.streetAddress addr_streetAddress, PA.colonyOrVillage addr_colonyOrVillage, PA.zone addr_zone, PA.district addr_district,\n PA.state addr_state, PA.country addr_country,\n PA.createdAt addr_createdAt, PA.updatedAt addr_updatedAt, PA.deletedAt addr_deletedAt,\n PP.number phoneNumber,\n B.identifier id_identifier, B.identifierType id_identifierType, B.searchHelp identifierSearchHelp, AF.name assignedFacilityName\n FROM Patient P\n INNER JOIN PatientAddress PA ON PA.uuid = P.addressUuid\n LEFT JOIN PatientPhoneNumber PP ON PP.patientUuid = P.uuid\n LEFT JOIN Facility AF ON AF.uuid = P.assignedFacilityId\n LEFT JOIN BusinessId B ON B.patientUuid = P.uuid\n WHERE P.deletedAt IS NULL"
+ }
+ ],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '5d488050f3e10d8e49723f7c5e4a1085')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/src/androidTest/java/org/simple/clinic/medicalhistory/MedicalHistoryRepositoryAndroidTest.kt b/app/src/androidTest/java/org/simple/clinic/medicalhistory/MedicalHistoryRepositoryAndroidTest.kt
index 766e09c6897..b988e67a86d 100644
--- a/app/src/androidTest/java/org/simple/clinic/medicalhistory/MedicalHistoryRepositoryAndroidTest.kt
+++ b/app/src/androidTest/java/org/simple/clinic/medicalhistory/MedicalHistoryRepositoryAndroidTest.kt
@@ -54,6 +54,7 @@ class MedicalHistoryRepositoryAndroidTest {
hasHadKidneyDisease = Yes,
hasDiabetes = No,
isSmoking = No,
+ isUsingSmokelessTobacco = No,
)
repository.save(
diff --git a/app/src/androidTest/java/org/simple/clinic/storage/migrations/Migration121AndroidTest.kt b/app/src/androidTest/java/org/simple/clinic/storage/migrations/Migration121AndroidTest.kt
new file mode 100644
index 00000000000..dcc4918f18f
--- /dev/null
+++ b/app/src/androidTest/java/org/simple/clinic/storage/migrations/Migration121AndroidTest.kt
@@ -0,0 +1,47 @@
+package org.simple.clinic.storage.migrations
+
+import org.junit.Test
+import org.simple.clinic.assertColumns
+
+class Migration121AndroidTest : BaseDatabaseMigrationTest(120, 121) {
+
+ @Test
+ fun isSmoking_should_be_added_to_medical_history_table() {
+ before.assertColumns("MedicalHistory", setOf(
+ "uuid",
+ "patientUuid",
+ "diagnosedWithHypertension",
+ "isOnHypertensionTreatment",
+ "isOnDiabetesTreatment",
+ "hasHadHeartAttack",
+ "hasHadStroke",
+ "hasHadKidneyDisease",
+ "hasDiabetes",
+ "isSmoking",
+ "cholesterol_value",
+ "syncStatus",
+ "createdAt",
+ "updatedAt",
+ "deletedAt"
+ ))
+
+ after.assertColumns("MedicalHistory", setOf(
+ "uuid",
+ "patientUuid",
+ "diagnosedWithHypertension",
+ "isOnHypertensionTreatment",
+ "isOnDiabetesTreatment",
+ "hasHadHeartAttack",
+ "hasHadStroke",
+ "hasHadKidneyDisease",
+ "hasDiabetes",
+ "isSmoking",
+ "isUsingSmokelessTobacco",
+ "cholesterol_value",
+ "syncStatus",
+ "createdAt",
+ "updatedAt",
+ "deletedAt"
+ ))
+ }
+}
diff --git a/app/src/debug/java/org/simple/clinic/DebugNotification.kt b/app/src/debug/java/org/simple/clinic/DebugNotification.kt
index f67d273ebf7..62c455a6d5a 100644
--- a/app/src/debug/java/org/simple/clinic/DebugNotification.kt
+++ b/app/src/debug/java/org/simple/clinic/DebugNotification.kt
@@ -6,7 +6,6 @@ import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
-import android.os.Build
import androidx.core.app.NotificationCompat
import org.simple.clinic.sync.DataSync
import javax.inject.Inject
@@ -19,15 +18,10 @@ object DebugNotification {
fun show(context: Context, appSignature: String) {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- notificationManager.createNotificationChannel(NotificationChannel(NOTIF_CHANNEL_ID, "Debug", NotificationManager.IMPORTANCE_MIN))
- }
+ notificationManager.createNotificationChannel(NotificationChannel(NOTIF_CHANNEL_ID, "Debug", NotificationManager.IMPORTANCE_MIN))
- val syncPendingIntentFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_CANCEL_CURRENT
- } else {
- PendingIntent.FLAG_CANCEL_CURRENT
- }
+ val syncPendingIntentFlags =
+ PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_CANCEL_CURRENT
val syncPendingIntent = PendingIntent.getBroadcast(
context,
0,
diff --git a/app/src/debug/res/drawable-v26/ic_launcher_foreground.xml b/app/src/debug/res/drawable/ic_launcher_foreground.xml
similarity index 100%
rename from app/src/debug/res/drawable-v26/ic_launcher_foreground.xml
rename to app/src/debug/res/drawable/ic_launcher_foreground.xml
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/debug/res/mipmap-anydpi/ic_launcher.xml
similarity index 79%
rename from app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
rename to app/src/debug/res/mipmap-anydpi/ic_launcher.xml
index a1bc384da78..370b2a4527c 100644
--- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ b/app/src/debug/res/mipmap-anydpi/ic_launcher.xml
@@ -2,4 +2,5 @@
+
diff --git a/app/src/debug/res/mipmap-hdpi/ic_launcher.png b/app/src/debug/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100755
index a9c770e85c6..00000000000
Binary files a/app/src/debug/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/debug/res/mipmap-mdpi/ic_launcher.png b/app/src/debug/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100755
index 1176e66cc7a..00000000000
Binary files a/app/src/debug/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/debug/res/mipmap-xhdpi/ic_launcher.png b/app/src/debug/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100755
index 87457839baf..00000000000
Binary files a/app/src/debug/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/debug/res/mipmap-xxhdpi/ic_launcher.png b/app/src/debug/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100755
index cd3300b7c00..00000000000
Binary files a/app/src/debug/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100755
index f6e225e8961..00000000000
Binary files a/app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d89719ef468..ee9fa727b43 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -58,8 +58,7 @@
android:supportsRtl="true"
android:theme="@style/Theme.Simple"
tools:ignore="AllowBackup,DataExtractionRules,GoogleAppIndexingWarning,LockedOrientationActivity"
- tools:replace="android:allowBackup"
- tools:targetApi="n">
+ tools:replace="android:allowBackup">
-
+
diff --git a/app/src/main/java/org/simple/clinic/AppDatabase.kt b/app/src/main/java/org/simple/clinic/AppDatabase.kt
index d62542bc7bc..ff54641be7a 100644
--- a/app/src/main/java/org/simple/clinic/AppDatabase.kt
+++ b/app/src/main/java/org/simple/clinic/AppDatabase.kt
@@ -110,7 +110,7 @@ import org.simple.clinic.patient.Answer as PatientAnswer
views = [
PatientSearchResult::class
],
- version = 120,
+ version = 121,
exportSchema = true
)
@TypeConverters(
diff --git a/app/src/main/java/org/simple/clinic/analytics/NetworkCapabilitiesProvider.kt b/app/src/main/java/org/simple/clinic/analytics/NetworkCapabilitiesProvider.kt
index 90d18892b39..106e17b2895 100644
--- a/app/src/main/java/org/simple/clinic/analytics/NetworkCapabilitiesProvider.kt
+++ b/app/src/main/java/org/simple/clinic/analytics/NetworkCapabilitiesProvider.kt
@@ -7,7 +7,6 @@ import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
-import android.os.Build
import org.simple.clinic.analytics.NetworkConnectivityStatus.ACTIVE
import org.simple.clinic.analytics.NetworkConnectivityStatus.INACTIVE
import javax.inject.Inject
@@ -16,11 +15,8 @@ class NetworkCapabilitiesProvider @Inject constructor(private val application: A
fun activeNetworkCapabilities(): NetworkCapabilities? {
val connectivityManager = application.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
- val currentActiveNetwork = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- connectivityManager.activeNetwork
- } else {
- findCurrentActiveNetworkV21(connectivityManager)
- }
+ val currentActiveNetwork =
+ connectivityManager.activeNetwork
return currentActiveNetwork?.let { network ->
connectivityManager.getNetworkCapabilities(network)
@@ -36,11 +32,8 @@ class NetworkCapabilitiesProvider @Inject constructor(private val application: A
fun networkConnectivityStatus(): NetworkConnectivityStatus {
val networkCapabilities = activeNetworkCapabilities() ?: return INACTIVE
val isConnectedToNetwork = networkCapabilities.hasCapability(NET_CAPABILITY_INTERNET)
- val isConnectedToInternet = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- isConnectedToNetwork && networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)
- } else {
- isConnectedToNetwork // Network validation check is not available before API 23
- }
+ val isConnectedToInternet =
+ isConnectedToNetwork && networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)
return if (isConnectedToInternet) {
ACTIVE
diff --git a/app/src/main/java/org/simple/clinic/appupdate/AppUpdateNotificationWorker.kt b/app/src/main/java/org/simple/clinic/appupdate/AppUpdateNotificationWorker.kt
index 6d2c1905358..a00e4f265e1 100644
--- a/app/src/main/java/org/simple/clinic/appupdate/AppUpdateNotificationWorker.kt
+++ b/app/src/main/java/org/simple/clinic/appupdate/AppUpdateNotificationWorker.kt
@@ -6,9 +6,8 @@ import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
-import android.net.Uri
-import android.os.Build
import androidx.core.app.NotificationCompat
+import androidx.core.net.toUri
import androidx.work.OneTimeWorkRequest
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.RxWorker
@@ -62,7 +61,7 @@ class AppUpdateNotificationWorker(
.build()
}
- private fun notificationScheduledTime(scheduledDateTime: LocalDateTime, currentDateTime: LocalDateTime): LocalDateTime {
+ private fun notificationScheduledTime(scheduledDateTime: LocalDateTime, currentDateTime: LocalDateTime): LocalDateTime {
return if (currentDateTime.isAfter(scheduledDateTime)) {
scheduledDateTime.plusDays(1)
} else {
@@ -104,7 +103,8 @@ class AppUpdateNotificationWorker(
when (result) {
is ShowAppUpdate -> showAppUpdateNotificationBasedOnThePriority(result)
is AppUpdateState.AppUpdateStateError -> resetPreferences()
- DontShowAppUpdate -> { /* no-op */ }
+ DontShowAppUpdate -> { /* no-op */
+ }
}.exhaustive()
Result.success()
@@ -139,13 +139,11 @@ class AppUpdateNotificationWorker(
}
private fun createNotificationChannel() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- notificationManager.createNotificationChannel(NotificationChannel(
- NOTIFICATION_CHANNEL_ID,
- NOTIFICATION_CHANNEL_NAME,
- NotificationManager.IMPORTANCE_HIGH
- ))
- }
+ notificationManager.createNotificationChannel(NotificationChannel(
+ NOTIFICATION_CHANNEL_ID,
+ NOTIFICATION_CHANNEL_NAME,
+ NotificationManager.IMPORTANCE_HIGH
+ ))
}
private fun showLightAppUpdateNotification() {
@@ -179,13 +177,10 @@ class AppUpdateNotificationWorker(
}
private fun openSimpleInPlayStorePendingIntent(): PendingIntent {
- val flag = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_CANCEL_CURRENT
- } else {
- PendingIntent.FLAG_CANCEL_CURRENT
- }
+ val flag =
+ PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_CANCEL_CURRENT
- val intent = Intent(Intent.ACTION_VIEW, Uri.parse(PLAY_STORE_URL_FOR_SIMPLE))
+ val intent = Intent(Intent.ACTION_VIEW, PLAY_STORE_URL_FOR_SIMPLE.toUri())
return PendingIntent.getActivity(context, 0, intent, flag)
}
diff --git a/app/src/main/java/org/simple/clinic/appupdate/criticalupdatedialog/CriticalAppUpdateDialog.kt b/app/src/main/java/org/simple/clinic/appupdate/criticalupdatedialog/CriticalAppUpdateDialog.kt
index 3790d31f83b..c68f15f351c 100644
--- a/app/src/main/java/org/simple/clinic/appupdate/criticalupdatedialog/CriticalAppUpdateDialog.kt
+++ b/app/src/main/java/org/simple/clinic/appupdate/criticalupdatedialog/CriticalAppUpdateDialog.kt
@@ -3,13 +3,13 @@ package org.simple.clinic.appupdate.criticalupdatedialog
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
-import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup
+import androidx.core.net.toUri
import com.jakewharton.rxbinding3.view.clicks
import com.spotify.mobius.functions.Consumer
import io.reactivex.Observable
@@ -148,7 +148,7 @@ class CriticalAppUpdateDialog : BaseDialog<
override fun openContactUrl(url: String) {
val packageManager = requireContext().packageManager
- val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
+ val intent = Intent(Intent.ACTION_VIEW, url.toUri())
if (intent.resolveActivity(packageManager) != null) {
requireContext().startActivity(intent)
@@ -159,7 +159,7 @@ class CriticalAppUpdateDialog : BaseDialog<
override fun openSimpleInGooglePlay() {
val packageManager = requireContext().packageManager
- val intent = Intent(Intent.ACTION_VIEW, Uri.parse(PLAY_STORE_URL_FOR_SIMPLE))
+ val intent = Intent(Intent.ACTION_VIEW, PLAY_STORE_URL_FOR_SIMPLE.toUri())
if (intent.resolveActivity(packageManager) != null) {
requireContext().startActivity(intent)
diff --git a/app/src/main/java/org/simple/clinic/appupdate/dialog/AppUpdateDialog.kt b/app/src/main/java/org/simple/clinic/appupdate/dialog/AppUpdateDialog.kt
index 6e06da4027d..f157528cd67 100644
--- a/app/src/main/java/org/simple/clinic/appupdate/dialog/AppUpdateDialog.kt
+++ b/app/src/main/java/org/simple/clinic/appupdate/dialog/AppUpdateDialog.kt
@@ -3,12 +3,11 @@ package org.simple.clinic.appupdate.dialog
import android.app.Dialog
import android.content.Intent
import android.content.Intent.ACTION_VIEW
-import android.net.Uri
import android.os.Bundle
+import androidx.core.net.toUri
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import org.simple.clinic.BuildConfig
import org.simple.clinic.PLAY_STORE_URL_FOR_SIMPLE
import org.simple.clinic.R
@@ -40,7 +39,7 @@ class AppUpdateDialog : DialogFragment() {
private fun launchPlayStoreForUpdate() {
val intent = Intent(ACTION_VIEW).apply {
- data = Uri.parse(PLAY_STORE_URL_FOR_SIMPLE)
+ data = PLAY_STORE_URL_FOR_SIMPLE.toUri()
}
startActivity(intent)
}
diff --git a/app/src/main/java/org/simple/clinic/drugstockreminders/DrugStockWorker.kt b/app/src/main/java/org/simple/clinic/drugstockreminders/DrugStockWorker.kt
index f0fc1138d23..01fc85bdb58 100644
--- a/app/src/main/java/org/simple/clinic/drugstockreminders/DrugStockWorker.kt
+++ b/app/src/main/java/org/simple/clinic/drugstockreminders/DrugStockWorker.kt
@@ -6,7 +6,6 @@ import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
-import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.work.OneTimeWorkRequest
import androidx.work.OneTimeWorkRequestBuilder
@@ -146,23 +145,17 @@ class DrugStockWorker(
private fun openAppToHomeScreen(): PendingIntent? {
val intent = Intent(context, SetupActivity::class.java)
- val flag = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
- } else {
- PendingIntent.FLAG_CANCEL_CURRENT
- }
+ val flag = PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
return PendingIntent.getActivity(context, 0, intent, flag)
}
private fun createNotificationChannel() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- notificationManager.createNotificationChannel(
- NotificationChannel(
- NOTIFICATION_CHANNEL_ID,
- NOTIFICATION_CHANNEL_NAME,
- NotificationManager.IMPORTANCE_HIGH
- ))
- }
+ notificationManager.createNotificationChannel(
+ NotificationChannel(
+ NOTIFICATION_CHANNEL_ID,
+ NOTIFICATION_CHANNEL_NAME,
+ NotificationManager.IMPORTANCE_HIGH
+ ))
}
}
diff --git a/app/src/main/java/org/simple/clinic/drugstockreminders/enterdrugstock/EnterDrugStockWebViewClient.kt b/app/src/main/java/org/simple/clinic/drugstockreminders/enterdrugstock/EnterDrugStockWebViewClient.kt
index 0d3563bab85..f600a3ce05c 100644
--- a/app/src/main/java/org/simple/clinic/drugstockreminders/enterdrugstock/EnterDrugStockWebViewClient.kt
+++ b/app/src/main/java/org/simple/clinic/drugstockreminders/enterdrugstock/EnterDrugStockWebViewClient.kt
@@ -1,11 +1,9 @@
package org.simple.clinic.drugstockreminders.enterdrugstock
-import android.os.Build
import android.webkit.WebResourceError
import android.webkit.WebResourceRequest
import android.webkit.WebView
import android.webkit.WebViewClient
-import androidx.annotation.RequiresApi
class EnterDrugStockWebViewClient(
private val backClicked: () -> Unit
@@ -20,7 +18,6 @@ class EnterDrugStockWebViewClient(
return false
}
- @RequiresApi(Build.VERSION_CODES.M)
override fun onReceivedError(
view: WebView?,
request: WebResourceRequest?,
diff --git a/app/src/main/java/org/simple/clinic/facilitypicker/FacilityPickerView.kt b/app/src/main/java/org/simple/clinic/facilitypicker/FacilityPickerView.kt
index 524ca849d64..59036782c39 100644
--- a/app/src/main/java/org/simple/clinic/facilitypicker/FacilityPickerView.kt
+++ b/app/src/main/java/org/simple/clinic/facilitypicker/FacilityPickerView.kt
@@ -6,7 +6,7 @@ import android.os.Parcelable
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.FrameLayout
-import android.widget.RelativeLayout
+import androidx.core.content.withStyledAttributes
import androidx.recyclerview.widget.LinearLayoutManager
import com.jakewharton.rxbinding3.recyclerview.scrollEvents
import com.jakewharton.rxbinding3.recyclerview.scrollStateChanges
@@ -65,7 +65,7 @@ class FacilityPickerView(
)
)
- private val pickFrom: PickFrom
+ private lateinit var pickFrom: PickFrom
private var binding: ViewFacilitypickerBinding? = null
@@ -88,9 +88,9 @@ class FacilityPickerView(
val layoutInflater = LayoutInflater.from(context)
binding = ViewFacilitypickerBinding.inflate(layoutInflater, this)
- val typedArray = context.obtainStyledAttributes(attributeSet, R.styleable.FacilityPickerView)
- pickFrom = PickFrom.forAttribute(typedArray)
- typedArray.recycle()
+ context.withStyledAttributes(attributeSet, R.styleable.FacilityPickerView) {
+ pickFrom = PickFrom.forAttribute(this)
+ }
context.injector().inject(this)
@@ -151,11 +151,11 @@ class FacilityPickerView(
}
override fun showProgressIndicator() {
- progressIndicator.visibility = RelativeLayout.VISIBLE
+ progressIndicator.visibility = VISIBLE
}
override fun hideProgressIndicator() {
- progressIndicator.visibility = RelativeLayout.GONE
+ progressIndicator.visibility = GONE
}
override fun updateFacilities(facilityItems: List) {
diff --git a/app/src/main/java/org/simple/clinic/feature/Feature.kt b/app/src/main/java/org/simple/clinic/feature/Feature.kt
index 7284d7634d5..f33ee931e20 100644
--- a/app/src/main/java/org/simple/clinic/feature/Feature.kt
+++ b/app/src/main/java/org/simple/clinic/feature/Feature.kt
@@ -1,7 +1,5 @@
package org.simple.clinic.feature
-import android.os.Build
-
enum class Feature(
val enabledByDefault: Boolean,
val remoteConfigKey: String = ""
@@ -19,7 +17,7 @@ enum class Feature(
* There's a corresponding activity [org.simple.clinic.WebviewTestActivity], which you
* can run to verify the fix.
**/
- ChangeLanguage(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M),
+ ChangeLanguage(true),
HttpRequestBodyCompression(false, "http_request_body_compression_enabled"),
CallResultSyncEnabled(true),
NotifyAppUpdateAvailableV2(false, "appupdate_enabled_v2"),
diff --git a/app/src/main/java/org/simple/clinic/home/HomeScreen.kt b/app/src/main/java/org/simple/clinic/home/HomeScreen.kt
index 1e3af2d6d01..266d04d47a9 100644
--- a/app/src/main/java/org/simple/clinic/home/HomeScreen.kt
+++ b/app/src/main/java/org/simple/clinic/home/HomeScreen.kt
@@ -51,8 +51,8 @@ import org.simple.clinic.settings.SettingsScreen
import org.simple.clinic.summary.OpenIntention
import org.simple.clinic.summary.PatientSummaryScreenKey
import org.simple.clinic.util.UtcClock
-import org.simple.clinic.util.exhaustive
import org.simple.clinic.util.applyStatusBarPadding
+import org.simple.clinic.util.exhaustive
import org.simple.clinic.widgets.findCurrentFragment
import org.simple.clinic.widgets.hideKeyboard
import java.time.Instant
@@ -207,6 +207,7 @@ class HomeScreen :
router.push(SettingsScreen.Key())
true
}
+
else -> false
}
}
diff --git a/app/src/main/java/org/simple/clinic/home/HomeScreenEvent.kt b/app/src/main/java/org/simple/clinic/home/HomeScreenEvent.kt
index e6599332722..70b5fb66b44 100644
--- a/app/src/main/java/org/simple/clinic/home/HomeScreenEvent.kt
+++ b/app/src/main/java/org/simple/clinic/home/HomeScreenEvent.kt
@@ -1,13 +1,10 @@
package org.simple.clinic.home
-import android.Manifest
-import android.os.Build
-import androidx.annotation.RequiresApi
-import java.util.Optional
import org.simple.clinic.activity.permissions.RequiresPermission
import org.simple.clinic.facility.Facility
import org.simple.clinic.platform.util.RuntimePermissionResult
import org.simple.clinic.widgets.UiEvent
+import java.util.Optional
sealed class HomeScreenEvent : UiEvent
diff --git a/app/src/main/java/org/simple/clinic/home/overdue/OverdueScreen.kt b/app/src/main/java/org/simple/clinic/home/overdue/OverdueScreen.kt
index fad3cba5950..8b08140c870 100644
--- a/app/src/main/java/org/simple/clinic/home/overdue/OverdueScreen.kt
+++ b/app/src/main/java/org/simple/clinic/home/overdue/OverdueScreen.kt
@@ -1,7 +1,6 @@
package org.simple.clinic.home.overdue
import android.content.Context
-import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -150,11 +149,7 @@ class OverdueScreen : BaseScreen<
override fun createUpdate(): Update {
val date = LocalDate.now(userClock)
- val canGeneratePdf = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
- return OverdueUpdate(
- date = date,
- canGeneratePdf = canGeneratePdf
- )
+ return OverdueUpdate(date = date, canGeneratePdf = true)
}
override fun createInit() = OverdueInit()
diff --git a/app/src/main/java/org/simple/clinic/home/overdue/search/OverdueSearchScreen.kt b/app/src/main/java/org/simple/clinic/home/overdue/search/OverdueSearchScreen.kt
index d76acc44de3..d97245c8435 100644
--- a/app/src/main/java/org/simple/clinic/home/overdue/search/OverdueSearchScreen.kt
+++ b/app/src/main/java/org/simple/clinic/home/overdue/search/OverdueSearchScreen.kt
@@ -1,7 +1,6 @@
package org.simple.clinic.home.overdue.search
import android.content.Context
-import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -166,9 +165,7 @@ class OverdueSearchScreen : BaseScreen<
override fun defaultModel() = OverdueSearchModel.create()
override fun createUpdate(): OverdueSearchUpdate {
- val canGeneratePdf = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
-
- return OverdueSearchUpdate(LocalDate.now(userClock), canGeneratePdf)
+ return OverdueSearchUpdate(LocalDate.now(userClock), true)
}
override fun createInit() = OverdueSearchInit()
diff --git a/app/src/main/java/org/simple/clinic/home/patients/PatientsTabScreen.kt b/app/src/main/java/org/simple/clinic/home/patients/PatientsTabScreen.kt
index 837cd788181..c87ec7d49b9 100644
--- a/app/src/main/java/org/simple/clinic/home/patients/PatientsTabScreen.kt
+++ b/app/src/main/java/org/simple/clinic/home/patients/PatientsTabScreen.kt
@@ -3,7 +3,6 @@ package org.simple.clinic.home.patients
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
-import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -11,6 +10,7 @@ import android.view.ViewGroup
import android.view.animation.AnimationUtils
import androidx.annotation.IdRes
import androidx.appcompat.app.AppCompatActivity
+import androidx.core.net.toUri
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.jakewharton.rxbinding3.view.clicks
@@ -336,7 +336,7 @@ class PatientsTabScreen : BaseScreen<
override fun openSimpleOnPlaystore() {
val packageManager = requireContext().packageManager
- val intent = Intent(Intent.ACTION_VIEW, Uri.parse(PLAY_STORE_URL_FOR_SIMPLE))
+ val intent = Intent(Intent.ACTION_VIEW, PLAY_STORE_URL_FOR_SIMPLE.toUri())
if (intent.resolveActivity(packageManager) != null) {
requireContext().startActivity(intent)
diff --git a/app/src/main/java/org/simple/clinic/home/report/ReportsWebViewClient.kt b/app/src/main/java/org/simple/clinic/home/report/ReportsWebViewClient.kt
index ccd67f58b66..29849ed7e0d 100644
--- a/app/src/main/java/org/simple/clinic/home/report/ReportsWebViewClient.kt
+++ b/app/src/main/java/org/simple/clinic/home/report/ReportsWebViewClient.kt
@@ -1,11 +1,9 @@
package org.simple.clinic.home.report
-import android.os.Build
import android.webkit.WebResourceError
import android.webkit.WebResourceRequest
import android.webkit.WebView
import android.webkit.WebViewClient
-import androidx.annotation.RequiresApi
class ReportsWebViewClient(
private val backClicked: () -> Unit
@@ -36,7 +34,6 @@ class ReportsWebViewClient(
super.onPageFinished(view, url)
}
- @RequiresApi(Build.VERSION_CODES.M)
override fun onReceivedError(
view: WebView?,
request: WebResourceRequest?,
diff --git a/app/src/main/java/org/simple/clinic/introvideoscreen/IntroVideoScreen.kt b/app/src/main/java/org/simple/clinic/introvideoscreen/IntroVideoScreen.kt
index f35c405ddb2..f5c20517462 100644
--- a/app/src/main/java/org/simple/clinic/introvideoscreen/IntroVideoScreen.kt
+++ b/app/src/main/java/org/simple/clinic/introvideoscreen/IntroVideoScreen.kt
@@ -3,11 +3,11 @@ package org.simple.clinic.introvideoscreen
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
-import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import androidx.core.net.toUri
import com.jakewharton.rxbinding3.view.clicks
import com.spotify.mobius.functions.Consumer
import io.reactivex.Observable
@@ -96,7 +96,7 @@ class IntroVideoScreen : BaseScreen<
private fun openYoutubeLinkForSimpleVideo() {
val packageManager = requireContext().packageManager
- val intent = Intent(Intent.ACTION_VIEW, Uri.parse(simpleVideo.url))
+ val intent = Intent(Intent.ACTION_VIEW, simpleVideo.url.toUri())
if (intent.resolveActivity(packageManager) != null) {
requireContext().startActivity(intent)
diff --git a/app/src/main/java/org/simple/clinic/medicalhistory/MedicalHistory.kt b/app/src/main/java/org/simple/clinic/medicalhistory/MedicalHistory.kt
index d0e9ce626c7..e4be1c248d9 100644
--- a/app/src/main/java/org/simple/clinic/medicalhistory/MedicalHistory.kt
+++ b/app/src/main/java/org/simple/clinic/medicalhistory/MedicalHistory.kt
@@ -22,6 +22,7 @@ import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.HasHadAStroke
import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.IsOnDiabetesTreatment
import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.IsOnHypertensionTreatment
import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.IsSmoking
+import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.IsUsingSmokelessTobacco
import org.simple.clinic.patient.PatientUuid
import org.simple.clinic.patient.SyncStatus
import java.time.Instant
@@ -57,6 +58,8 @@ data class MedicalHistory(
val isSmoking: Answer,
+ val isUsingSmokelessTobacco: Answer,
+
@ColumnInfo(name = "cholesterol_value")
val cholesterol: Float?,
@@ -89,6 +92,7 @@ data class MedicalHistory(
is IsOnHypertensionTreatment -> copy(isOnHypertensionTreatment = answer)
IsOnDiabetesTreatment -> copy(isOnDiabetesTreatment = answer)
IsSmoking -> copy(isSmoking = answer)
+ IsUsingSmokelessTobacco -> copy(isUsingSmokelessTobacco = answer)
}
}
diff --git a/app/src/main/java/org/simple/clinic/medicalhistory/MedicalHistoryQuestion.kt b/app/src/main/java/org/simple/clinic/medicalhistory/MedicalHistoryQuestion.kt
index 47d85628b12..e319c7c6e9e 100644
--- a/app/src/main/java/org/simple/clinic/medicalhistory/MedicalHistoryQuestion.kt
+++ b/app/src/main/java/org/simple/clinic/medicalhistory/MedicalHistoryQuestion.kt
@@ -22,5 +22,6 @@ sealed class MedicalHistoryQuestion(
)
object IsOnDiabetesTreatment : MedicalHistoryQuestion(R.string.medicalhistory_question_is_on_diabetes_treatment)
- data object IsSmoking : MedicalHistoryQuestion(R.string.medicalhistory_question_is_current_smoker)
+ data object IsSmoking : MedicalHistoryQuestion(R.string.medicalhistory_question_is_smoking)
+ data object IsUsingSmokelessTobacco : MedicalHistoryQuestion(R.string.medicalhistory_question_is_using_smokeless_tobacco)
}
diff --git a/app/src/main/java/org/simple/clinic/medicalhistory/MedicalHistoryRepository.kt b/app/src/main/java/org/simple/clinic/medicalhistory/MedicalHistoryRepository.kt
index 85da9f5bf8d..64b3095ce5c 100644
--- a/app/src/main/java/org/simple/clinic/medicalhistory/MedicalHistoryRepository.kt
+++ b/app/src/main/java/org/simple/clinic/medicalhistory/MedicalHistoryRepository.kt
@@ -6,7 +6,6 @@ import org.simple.clinic.medicalhistory.Answer.Unanswered
import org.simple.clinic.medicalhistory.sync.MedicalHistoryPayload
import org.simple.clinic.patient.PatientUuid
import org.simple.clinic.patient.SyncStatus
-import org.simple.clinic.patient.SyncStatus.PENDING
import org.simple.clinic.sync.SynceableRepository
import org.simple.clinic.util.UtcClock
import java.time.Instant
@@ -34,6 +33,7 @@ class MedicalHistoryRepository @Inject constructor(
hasHadKidneyDisease = Unanswered,
diagnosedWithDiabetes = Unanswered,
isSmoking = Unanswered,
+ isUsingSmokelessTobacco = Unanswered,
cholesterol = null,
syncStatus = SyncStatus.DONE,
createdAt = Instant.now(utcClock),
@@ -75,6 +75,7 @@ class MedicalHistoryRepository @Inject constructor(
hasHadKidneyDisease = Unanswered,
diagnosedWithDiabetes = Unanswered,
isSmoking = Unanswered,
+ isUsingSmokelessTobacco = Unanswered,
cholesterol = null,
syncStatus = SyncStatus.DONE,
createdAt = Instant.now(utcClock),
@@ -100,6 +101,7 @@ class MedicalHistoryRepository @Inject constructor(
hasHadKidneyDisease = historyEntry.hasHadKidneyDisease,
diagnosedWithDiabetes = historyEntry.hasDiabetes,
isSmoking = historyEntry.isSmoking,
+ isUsingSmokelessTobacco = historyEntry.isUsingSmokelessTobacco,
cholesterol = null,
syncStatus = SyncStatus.PENDING,
createdAt = Instant.now(utcClock),
@@ -166,6 +168,7 @@ class MedicalHistoryRepository @Inject constructor(
hasHadKidneyDisease = hasHadKidneyDisease,
diagnosedWithDiabetes = hasDiabetes,
isSmoking = isSmoking,
+ isUsingSmokelessTobacco = isUsingSmokelessTobacco,
cholesterol = cholesterol,
syncStatus = syncStatus,
createdAt = createdAt,
diff --git a/app/src/main/java/org/simple/clinic/medicalhistory/OngoingMedicalHistoryEntry.kt b/app/src/main/java/org/simple/clinic/medicalhistory/OngoingMedicalHistoryEntry.kt
index e355d63c600..1aa858154cf 100644
--- a/app/src/main/java/org/simple/clinic/medicalhistory/OngoingMedicalHistoryEntry.kt
+++ b/app/src/main/java/org/simple/clinic/medicalhistory/OngoingMedicalHistoryEntry.kt
@@ -11,6 +11,7 @@ import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.HasHadAStroke
import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.IsOnDiabetesTreatment
import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.IsOnHypertensionTreatment
import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.IsSmoking
+import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.IsUsingSmokelessTobacco
@Parcelize
data class OngoingMedicalHistoryEntry(
@@ -22,6 +23,7 @@ data class OngoingMedicalHistoryEntry(
val isOnDiabetesTreatment: Answer = Unanswered,
val hasDiabetes: Answer = Unanswered,
val isSmoking: Answer = Unanswered,
+ val isUsingSmokelessTobacco: Answer = Unanswered,
) : Parcelable {
fun answerChanged(question: MedicalHistoryQuestion, answer: Answer): OngoingMedicalHistoryEntry {
@@ -34,6 +36,7 @@ data class OngoingMedicalHistoryEntry(
is IsOnHypertensionTreatment -> copy(isOnHypertensionTreatment = answer)
IsOnDiabetesTreatment -> copy(isOnDiabetesTreatment = answer)
IsSmoking -> copy(isSmoking = answer)
+ IsUsingSmokelessTobacco -> copy(isUsingSmokelessTobacco = answer)
}
}
}
diff --git a/app/src/main/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryModel.kt b/app/src/main/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryModel.kt
index 8ad94862a7f..4787d13f24c 100644
--- a/app/src/main/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryModel.kt
+++ b/app/src/main/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryModel.kt
@@ -21,6 +21,7 @@ data class NewMedicalHistoryModel(
val nextButtonState: ButtonState?,
val hasShownChangeDiagnosisError: Boolean,
val showIsSmokingQuestion: Boolean,
+ val showSmokelessTobaccoQuestion: Boolean,
) : Parcelable {
val hasLoadedPatientEntry: Boolean
@@ -72,14 +73,19 @@ data class NewMedicalHistoryModel(
get() = facilityDiabetesManagementEnabled && !hasShownChangeDiagnosisError && hasNoHypertension && hasNoDiabetes
companion object {
- fun default(country: Country, showIsSmokingQuestion: Boolean): NewMedicalHistoryModel = NewMedicalHistoryModel(
+ fun default(
+ country: Country,
+ showIsSmokingQuestion: Boolean,
+ showSmokelessTobaccoQuestion: Boolean
+ ): NewMedicalHistoryModel = NewMedicalHistoryModel(
country = country,
ongoingPatientEntry = null,
ongoingMedicalHistoryEntry = OngoingMedicalHistoryEntry(),
currentFacility = null,
nextButtonState = null,
hasShownChangeDiagnosisError = false,
- showIsSmokingQuestion = showIsSmokingQuestion
+ showIsSmokingQuestion = showIsSmokingQuestion,
+ showSmokelessTobaccoQuestion = showSmokelessTobaccoQuestion
)
}
diff --git a/app/src/main/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryScreen.kt b/app/src/main/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryScreen.kt
index 2420022f2ff..8aa544b7986 100644
--- a/app/src/main/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryScreen.kt
+++ b/app/src/main/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryScreen.kt
@@ -8,6 +8,15 @@ import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.ViewCompositionStrategy
+import androidx.compose.ui.res.dimensionResource
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.jakewharton.rxbinding3.view.clicks
import com.spotify.mobius.functions.Consumer
@@ -19,6 +28,7 @@ import kotlinx.parcelize.Parcelize
import org.simple.clinic.R
import org.simple.clinic.ReportAnalyticsEvents
import org.simple.clinic.appconfig.Country
+import org.simple.clinic.common.ui.theme.SimpleTheme
import org.simple.clinic.databinding.ScreenNewMedicalHistoryBinding
import org.simple.clinic.di.injector
import org.simple.clinic.feature.Feature
@@ -32,10 +42,10 @@ import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.HasHadAKidneyDise
import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.HasHadAStroke
import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.IsOnDiabetesTreatment
import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.IsOnHypertensionTreatment
-import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.IsSmoking
import org.simple.clinic.medicalhistory.SelectDiagnosisErrorDialog
import org.simple.clinic.medicalhistory.SelectOngoingDiabetesTreatmentErrorDialog
import org.simple.clinic.medicalhistory.SelectOngoingHypertensionTreatmentErrorDialog
+import org.simple.clinic.medicalhistory.ui.TobaccoQuestion
import org.simple.clinic.navigation.v2.Router
import org.simple.clinic.navigation.v2.ScreenKey
import org.simple.clinic.navigation.v2.fragments.BaseScreen
@@ -100,27 +110,27 @@ class NewMedicalHistoryScreen : BaseScreen<
private val diabetesQuestionView
get() = binding.diabetesQuestionView
- private val scrollView
- get() = binding.scrollView
-
private val hypertensionDiagnosis
get() = binding.hypertensionDiagnosis
private val diabetesDiagnosis
get() = binding.diabetesDiagnosis
- private val currentSmokerQuestionContainer
- get() = binding.currentSmokerQuestionContainer
+ private var showSmokerQuestion by mutableStateOf(false)
+ private var isSmoking by mutableStateOf(Answer.Unanswered)
+ private var showSmokelessTobaccoQuestion by mutableStateOf(false)
+ private var isUsingSmokelessTobacco by mutableStateOf(Answer.Unanswered)
- private val currentSmokerQuestionView
- get() = binding.currentSmokerQuestionView
+ private val composeView
+ get() = binding.composeView
private val hotEvents: Subject = PublishSubject.create()
override fun defaultModel() = NewMedicalHistoryModel.default(
country = country,
showIsSmokingQuestion = features.isEnabled(Feature.NonLabBasedStatinNudge) ||
- features.isEnabled(Feature.LabBasedStatinNudge)
+ features.isEnabled(Feature.LabBasedStatinNudge),
+ showSmokelessTobaccoQuestion = country.isoCountryCode != Country.ETHIOPIA
)
override fun bindView(
@@ -164,6 +174,31 @@ class NewMedicalHistoryScreen : BaseScreen<
toolbar.setNavigationOnClickListener {
router.pop()
}
+
+ composeView.apply {
+ setViewCompositionStrategy(
+ ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
+ )
+ setContent {
+ SimpleTheme {
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(dimensionResource(R.dimen.spacing_8)),
+ ) {
+ if (showSmokerQuestion) {
+ TobaccoQuestion(
+ isSmokingAnswer = isSmoking,
+ isUsingSmokelessTobaccoAnswer = isUsingSmokelessTobacco,
+ showSmokelessTobaccoQuestion = showSmokelessTobaccoQuestion,
+ ) { question, answer ->
+ hotEvents.onNext(NewMedicalHistoryAnswerToggled(question, answer))
+ }
+ }
+ }
+ }
+ }
+ }
}
private fun saveClicks() = nextButton
@@ -184,13 +219,20 @@ class NewMedicalHistoryScreen : BaseScreen<
HasHadAStroke -> strokeQuestionView
HasHadAKidneyDisease -> kidneyDiseaseQuestionView
DiagnosedWithDiabetes -> diabetesQuestionView
- IsSmoking -> currentSmokerQuestionView
else -> null
}
view?.render(question, answer) { questionForView, newAnswer ->
hotEvents.onNext(NewMedicalHistoryAnswerToggled(questionForView, newAnswer))
}
+
+ if (question == MedicalHistoryQuestion.IsSmoking) {
+ isSmoking = answer
+ }
+
+ if (question == MedicalHistoryQuestion.IsUsingSmokelessTobacco) {
+ isUsingSmokelessTobacco = answer
+ }
}
override fun showDiabetesDiagnosisView() {
@@ -268,12 +310,19 @@ class NewMedicalHistoryScreen : BaseScreen<
}
override fun showCurrentSmokerQuestion() {
- currentSmokerQuestionContainer.visibility = VISIBLE
- currentSmokerQuestionView.hideDivider()
+ showSmokerQuestion = true
}
override fun hideCurrentSmokerQuestion() {
- currentSmokerQuestionContainer.visibility = GONE
+ showSmokerQuestion = false
+ }
+
+ override fun showSmokelessTobaccoQuestion() {
+ showSmokelessTobaccoQuestion = true
+ }
+
+ override fun hideSmokelessTobaccoQuestion() {
+ showSmokelessTobaccoQuestion = false
}
override fun showOngoingHypertensionTreatmentErrorDialog() {
diff --git a/app/src/main/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryUi.kt b/app/src/main/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryUi.kt
index 671b0b441f1..792c36b7bec 100644
--- a/app/src/main/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryUi.kt
+++ b/app/src/main/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryUi.kt
@@ -19,4 +19,6 @@ interface NewMedicalHistoryUi {
fun hideDiabetesTreatmentQuestion()
fun showCurrentSmokerQuestion()
fun hideCurrentSmokerQuestion()
+ fun showSmokelessTobaccoQuestion()
+ fun hideSmokelessTobaccoQuestion()
}
diff --git a/app/src/main/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryUiRenderer.kt b/app/src/main/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryUiRenderer.kt
index 6950be3518f..c8f322ec8c6 100644
--- a/app/src/main/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryUiRenderer.kt
+++ b/app/src/main/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryUiRenderer.kt
@@ -6,6 +6,7 @@ import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.HasHadAHeartAttac
import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.HasHadAKidneyDisease
import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.HasHadAStroke
import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.IsSmoking
+import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.IsUsingSmokelessTobacco
import org.simple.clinic.mobius.ViewRenderer
class NewMedicalHistoryUiRenderer(
@@ -26,6 +27,7 @@ class NewMedicalHistoryUiRenderer(
}
renderSmokingQuestion(model)
+ renderSmokelessTobaccoQuestion(model)
renderNextButton(model)
}
@@ -82,6 +84,15 @@ class NewMedicalHistoryUiRenderer(
}
}
+ private fun renderSmokelessTobaccoQuestion(model: NewMedicalHistoryModel) {
+ if (model.showSmokelessTobaccoQuestion) {
+ ui.showSmokelessTobaccoQuestion()
+ ui.renderAnswerForQuestion(IsUsingSmokelessTobacco, model.ongoingMedicalHistoryEntry.isUsingSmokelessTobacco)
+ } else {
+ ui.hideSmokelessTobaccoQuestion()
+ }
+ }
+
private fun renderNextButton(model: NewMedicalHistoryModel) {
if (model.registeringPatient) {
ui.showNextButtonProgress()
diff --git a/app/src/main/java/org/simple/clinic/medicalhistory/sync/MedicalHistoryPayload.kt b/app/src/main/java/org/simple/clinic/medicalhistory/sync/MedicalHistoryPayload.kt
index 45f70f140e8..65d58c935f5 100644
--- a/app/src/main/java/org/simple/clinic/medicalhistory/sync/MedicalHistoryPayload.kt
+++ b/app/src/main/java/org/simple/clinic/medicalhistory/sync/MedicalHistoryPayload.kt
@@ -46,6 +46,9 @@ data class MedicalHistoryPayload(
@Json(name = "smoking")
val isSmoking: Answer,
+ @Json(name = "smokeless_tobacco")
+ val isUsingSmokelessTobacco: Answer,
+
@Json(name = "cholesterol")
val cholesterol: Float?,
diff --git a/app/src/main/java/org/simple/clinic/medicalhistory/sync/MedicalHistorySync.kt b/app/src/main/java/org/simple/clinic/medicalhistory/sync/MedicalHistorySync.kt
index 32d68d35d40..a217dc60f9e 100644
--- a/app/src/main/java/org/simple/clinic/medicalhistory/sync/MedicalHistorySync.kt
+++ b/app/src/main/java/org/simple/clinic/medicalhistory/sync/MedicalHistorySync.kt
@@ -50,6 +50,7 @@ class MedicalHistorySync @Inject constructor(
hasDiabetes = diagnosedWithDiabetes,
hasHypertension = diagnosedWithHypertension,
isSmoking = isSmoking,
+ isUsingSmokelessTobacco = isUsingSmokelessTobacco,
cholesterol = cholesterol,
createdAt = createdAt,
updatedAt = updatedAt,
diff --git a/app/src/main/java/org/simple/clinic/medicalhistory/ui/TobaccoQuestion.kt b/app/src/main/java/org/simple/clinic/medicalhistory/ui/TobaccoQuestion.kt
new file mode 100644
index 00000000000..57676f5724e
--- /dev/null
+++ b/app/src/main/java/org/simple/clinic/medicalhistory/ui/TobaccoQuestion.kt
@@ -0,0 +1,128 @@
+package org.simple.clinic.medicalhistory.ui
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.requiredHeight
+import androidx.compose.material.Card
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import org.simple.clinic.R
+import org.simple.clinic.common.ui.theme.SimpleTheme
+import org.simple.clinic.medicalhistory.Answer
+import org.simple.clinic.medicalhistory.MedicalHistoryQuestion
+
+@Composable
+fun TobaccoQuestion(
+ isSmokingAnswer: Answer?,
+ isUsingSmokelessTobaccoAnswer: Answer?,
+ showSmokelessTobaccoQuestion: Boolean,
+ onAnswerChange: (MedicalHistoryQuestion, Answer) -> Unit,
+) {
+ if (showSmokelessTobaccoQuestion) {
+ TobaccoUseContainer(
+ isSmokingAnswer = isSmokingAnswer,
+ isUsingSmokelessTobaccoAnswer = isUsingSmokelessTobaccoAnswer,
+ onAnswerChange = onAnswerChange
+ )
+ } else {
+ SmokerContainer(
+ smokerAnswer = isSmokingAnswer,
+ onAnswerChange = onAnswerChange
+ )
+ }
+}
+
+@Composable
+fun SmokerContainer(
+ smokerAnswer: Answer?,
+ modifier: Modifier = Modifier,
+ onAnswerChange: (MedicalHistoryQuestion, Answer) -> Unit,
+) {
+ Card(modifier = modifier) {
+ MedicalHistoryQuestionItem(
+ modifier = Modifier
+ .padding(horizontal = dimensionResource(R.dimen.spacing_16))
+ .padding(vertical = dimensionResource(R.dimen.spacing_8)),
+ question = MedicalHistoryQuestion.IsSmoking,
+ selectedAnswer = smokerAnswer,
+ showDivider = false,
+ onSelectionChange = {
+ onAnswerChange(MedicalHistoryQuestion.IsSmoking, it)
+ }
+ )
+ }
+}
+
+@Composable
+fun TobaccoUseContainer(
+ isSmokingAnswer: Answer?,
+ isUsingSmokelessTobaccoAnswer: Answer?,
+ modifier: Modifier = Modifier,
+ onAnswerChange: (MedicalHistoryQuestion, Answer) -> Unit,
+) {
+ Card(modifier = modifier) {
+ Column(
+ modifier = Modifier
+ .padding(horizontal = dimensionResource(R.dimen.spacing_16))
+ .padding(
+ top = dimensionResource(R.dimen.spacing_16),
+ bottom = dimensionResource(R.dimen.spacing_4)
+ )
+ ) {
+ Text(
+ text = stringResource(R.string.medicalhistorysummaryview_tobacco_use),
+ style = SimpleTheme.typography.subtitle1Medium,
+ color = MaterialTheme.colors.onSurface,
+ )
+
+ Spacer(Modifier.requiredHeight(dimensionResource(R.dimen.spacing_4)))
+
+ MedicalHistoryQuestionItem(
+ question = MedicalHistoryQuestion.IsSmoking,
+ selectedAnswer = isSmokingAnswer,
+ showDivider = true,
+ ) {
+ onAnswerChange(MedicalHistoryQuestion.IsSmoking, it)
+ }
+
+ MedicalHistoryQuestionItem(
+ question = MedicalHistoryQuestion.IsUsingSmokelessTobacco,
+ selectedAnswer = isUsingSmokelessTobaccoAnswer,
+ showDivider = false,
+ ) {
+ onAnswerChange(MedicalHistoryQuestion.IsUsingSmokelessTobacco, it)
+ }
+ }
+ }
+}
+
+@Preview
+@Composable
+fun TobaccoQuestionPreview() {
+ TobaccoQuestion(
+ isSmokingAnswer = Answer.Yes,
+ isUsingSmokelessTobaccoAnswer = Answer.Unanswered,
+ showSmokelessTobaccoQuestion = false
+ ) { _, _ ->
+ //no-op
+ }
+}
+
+@Preview
+@Composable
+fun TobaccoWithSmokelessQuestionPreview() {
+ TobaccoQuestion(
+ isSmokingAnswer = Answer.Yes,
+ isUsingSmokelessTobaccoAnswer = Answer.Yes,
+ showSmokelessTobaccoQuestion = true
+ ) { _, _ ->
+ //no-op
+ }
+}
+
diff --git a/app/src/main/java/org/simple/clinic/monthlyreports/form/compose/util/ColorUtil.kt b/app/src/main/java/org/simple/clinic/monthlyreports/form/compose/util/ColorUtil.kt
index 733ae1384fb..b6e0ae7a4b4 100644
--- a/app/src/main/java/org/simple/clinic/monthlyreports/form/compose/util/ColorUtil.kt
+++ b/app/src/main/java/org/simple/clinic/monthlyreports/form/compose/util/ColorUtil.kt
@@ -3,12 +3,13 @@ package org.simple.clinic.monthlyreports.form.compose.util
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource
+import androidx.core.graphics.toColorInt
import org.simple.clinic.R
@Composable
fun getColor(colorString: String): Color {
return try {
- Color(android.graphics.Color.parseColor(colorString))
+ Color(colorString.toColorInt())
} catch (_: Throwable) {
colorResource(id = R.color.simple_dark_grey)
}
diff --git a/app/src/main/java/org/simple/clinic/newentry/PatientEntryScreen.kt b/app/src/main/java/org/simple/clinic/newentry/PatientEntryScreen.kt
index 23f111ced54..378ca861271 100644
--- a/app/src/main/java/org/simple/clinic/newentry/PatientEntryScreen.kt
+++ b/app/src/main/java/org/simple/clinic/newentry/PatientEntryScreen.kt
@@ -13,6 +13,7 @@ import android.widget.EditText
import android.widget.RadioButton
import androidx.core.text.buildSpannedString
import androidx.core.text.inSpans
+import androidx.core.view.isVisible
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import androidx.transition.ChangeBounds
import androidx.transition.Fade
@@ -243,7 +244,7 @@ class PatientEntryScreen : BaseScreen<
// FIXME This is temporally coupled to `scrollToFirstFieldWithError()`.
private val allTextInputFields: List by unsafeLazy {
- val ageOrDateOfBirthEditText = if (ageEditTextInputLayout.visibility == View.VISIBLE) {
+ val ageOrDateOfBirthEditText = if (ageEditTextInputLayout.isVisible) {
ageEditText
} else {
dateOfBirthEditText
@@ -697,7 +698,7 @@ class PatientEntryScreen : BaseScreen<
val firstFieldWithError = views
.filter {
when {
- isGenderErrorView(it) -> it.visibility == View.VISIBLE
+ isGenderErrorView(it) -> it.isVisible
it is TextInputLayout -> it.error.isNullOrBlank().not()
else -> throw AssertionError()
}
diff --git a/app/src/main/java/org/simple/clinic/overdue/download/OverdueDownloadWorker.kt b/app/src/main/java/org/simple/clinic/overdue/download/OverdueDownloadWorker.kt
index f27b8ae55f0..807488ba078 100644
--- a/app/src/main/java/org/simple/clinic/overdue/download/OverdueDownloadWorker.kt
+++ b/app/src/main/java/org/simple/clinic/overdue/download/OverdueDownloadWorker.kt
@@ -10,6 +10,7 @@ import android.content.pm.ServiceInfo
import android.net.Uri
import android.os.Build
import androidx.core.app.NotificationCompat
+import androidx.core.net.toUri
import androidx.work.ForegroundInfo
import androidx.work.OneTimeWorkRequest
import androidx.work.OneTimeWorkRequestBuilder
@@ -122,13 +123,11 @@ class OverdueDownloadWorker(
}
private fun createNotificationChannel() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- notificationManager.createNotificationChannel(NotificationChannel(
- NOTIFICATION_CHANNEL_ID,
- NOTIFICATION_CHANNEL_NAME,
- NotificationManager.IMPORTANCE_HIGH
- ))
- }
+ notificationManager.createNotificationChannel(NotificationChannel(
+ NOTIFICATION_CHANNEL_ID,
+ NOTIFICATION_CHANNEL_NAME,
+ NotificationManager.IMPORTANCE_HIGH
+ ))
}
private fun downloadInProgressNotification(): ForegroundInfo {
@@ -166,11 +165,8 @@ class OverdueDownloadWorker(
}
private fun downloadSucceededNotification(uri: Uri, fileFormat: OverdueListFileFormat): Notification {
- val flag = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_CANCEL_CURRENT
- } else {
- PendingIntent.FLAG_CANCEL_CURRENT
- }
+ val flag =
+ PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_CANCEL_CURRENT
var intent = Intent(Intent.ACTION_VIEW).apply {
setDataAndType(uri, fileFormat.mimeType)
@@ -185,7 +181,7 @@ class OverdueDownloadWorker(
if (fileFormat == CSV) intent.setPackage(GOOGLE_SHEETS_PACKAGE_NAME)
if (intent.resolveActivity(context.packageManager) == null) {
- intent = Intent(Intent.ACTION_VIEW, Uri.parse(playStoreUrl))
+ intent = Intent(Intent.ACTION_VIEW, playStoreUrl.toUri())
}
val pendingIntent = PendingIntent.getActivity(context, 0, intent, flag)
diff --git a/app/src/main/java/org/simple/clinic/patient/download/PatientLinetListDownloadWorker.kt b/app/src/main/java/org/simple/clinic/patient/download/PatientLinetListDownloadWorker.kt
index 751ac1de655..d4bf051b716 100644
--- a/app/src/main/java/org/simple/clinic/patient/download/PatientLinetListDownloadWorker.kt
+++ b/app/src/main/java/org/simple/clinic/patient/download/PatientLinetListDownloadWorker.kt
@@ -10,6 +10,7 @@ import android.content.pm.ServiceInfo
import android.net.Uri
import android.os.Build
import androidx.core.app.NotificationCompat
+import androidx.core.net.toUri
import androidx.work.ForegroundInfo
import androidx.work.OneTimeWorkRequest
import androidx.work.OneTimeWorkRequestBuilder
@@ -125,13 +126,11 @@ class PatientLinetListDownloadWorker(
}
private fun createNotificationChannel() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- notificationManager.createNotificationChannel(NotificationChannel(
- NOTIFICATION_CHANNEL_ID,
- NOTIFICATION_CHANNEL_NAME,
- NotificationManager.IMPORTANCE_HIGH
- ))
- }
+ notificationManager.createNotificationChannel(NotificationChannel(
+ NOTIFICATION_CHANNEL_ID,
+ NOTIFICATION_CHANNEL_NAME,
+ NotificationManager.IMPORTANCE_HIGH
+ ))
}
private fun downloadInProgressNotification(): ForegroundInfo {
@@ -169,11 +168,8 @@ class PatientLinetListDownloadWorker(
}
private fun downloadSucceededNotification(uri: Uri, fileFormat: PatientLineListFileFormat): Notification {
- val flag = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_CANCEL_CURRENT
- } else {
- PendingIntent.FLAG_CANCEL_CURRENT
- }
+ val flag =
+ PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_CANCEL_CURRENT
var intent = Intent(Intent.ACTION_VIEW).apply {
setDataAndType(uri, fileFormat.mimeType)
@@ -188,7 +184,7 @@ class PatientLinetListDownloadWorker(
if (fileFormat == CSV) intent.setPackage(GOOGLE_SHEETS_PACKAGE_NAME)
if (intent.resolveActivity(context.packageManager) == null) {
- intent = Intent(Intent.ACTION_VIEW, Uri.parse(playStoreUrl))
+ intent = Intent(Intent.ACTION_VIEW, playStoreUrl.toUri())
}
val pendingIntent = PendingIntent.getActivity(context, 0, intent, flag)
diff --git a/app/src/main/java/org/simple/clinic/patient/onlinelookup/api/LookupPatientOnline.kt b/app/src/main/java/org/simple/clinic/patient/onlinelookup/api/LookupPatientOnline.kt
index c7e8556da86..4c9a48f4674 100644
--- a/app/src/main/java/org/simple/clinic/patient/onlinelookup/api/LookupPatientOnline.kt
+++ b/app/src/main/java/org/simple/clinic/patient/onlinelookup/api/LookupPatientOnline.kt
@@ -138,6 +138,7 @@ class LookupPatientOnline @Inject constructor(
hasHadKidneyDisease = response.medicalHistory.hasHadKidneyDisease,
diagnosedWithDiabetes = response.medicalHistory.hasDiabetes,
isSmoking = response.medicalHistory.isSmoking,
+ isUsingSmokelessTobacco = response.medicalHistory.isUsingSmokelessTobacco,
cholesterol = response.medicalHistory.cholesterol,
syncStatus = SyncStatus.DONE,
createdAt = response.medicalHistory.createdAt,
diff --git a/app/src/main/java/org/simple/clinic/purge/PurgeWorker.kt b/app/src/main/java/org/simple/clinic/purge/PurgeWorker.kt
index 4f347c64afe..51666ec0e9e 100644
--- a/app/src/main/java/org/simple/clinic/purge/PurgeWorker.kt
+++ b/app/src/main/java/org/simple/clinic/purge/PurgeWorker.kt
@@ -5,7 +5,6 @@ import android.app.NotificationManager
import android.content.Context
import android.content.pm.ServiceInfo
import android.os.Build
-import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import androidx.work.ForegroundInfo
import androidx.work.OneTimeWorkRequest
@@ -78,9 +77,7 @@ class PurgeWorker(
}
private fun createForegroundInfo(): ForegroundInfo {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- createChannel()
- }
+ createChannel()
val notification = NotificationCompat.Builder(applicationContext, NOTIFICATION_CHANNEL_ID)
.setContentTitle(applicationContext.getString(R.string.purge_notification_title))
@@ -97,7 +94,6 @@ class PurgeWorker(
}
}
- @RequiresApi(Build.VERSION_CODES.O)
private fun createChannel() {
notificationManager.createNotificationChannel(NotificationChannel(
NOTIFICATION_CHANNEL_ID,
diff --git a/app/src/main/java/org/simple/clinic/scanid/qrcodeanalyzer/ZxingQrCodeAnalyzer.kt b/app/src/main/java/org/simple/clinic/scanid/qrcodeanalyzer/ZxingQrCodeAnalyzer.kt
index 42a59e3f916..ecd8de9a020 100644
--- a/app/src/main/java/org/simple/clinic/scanid/qrcodeanalyzer/ZxingQrCodeAnalyzer.kt
+++ b/app/src/main/java/org/simple/clinic/scanid/qrcodeanalyzer/ZxingQrCodeAnalyzer.kt
@@ -3,7 +3,6 @@ package org.simple.clinic.scanid.qrcodeanalyzer
import android.graphics.ImageFormat.YUV_420_888
import android.graphics.ImageFormat.YUV_422_888
import android.graphics.ImageFormat.YUV_444_888
-import android.os.Build
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageProxy
import com.google.zxing.BarcodeFormat
@@ -35,9 +34,7 @@ class ZxingQrCodeAnalyzer(
private val yuvFormats = mutableListOf(YUV_420_888)
init {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- yuvFormats.addAll(listOf(YUV_422_888, YUV_444_888))
- }
+ yuvFormats.addAll(listOf(YUV_422_888, YUV_444_888))
}
override fun analyze(image: ImageProxy) {
diff --git a/app/src/main/java/org/simple/clinic/security/pin/PinEntryCardView.kt b/app/src/main/java/org/simple/clinic/security/pin/PinEntryCardView.kt
index 7056721a88b..781b3bb2383 100644
--- a/app/src/main/java/org/simple/clinic/security/pin/PinEntryCardView.kt
+++ b/app/src/main/java/org/simple/clinic/security/pin/PinEntryCardView.kt
@@ -6,8 +6,8 @@ import android.os.CountDownTimer
import android.os.Parcelable
import android.util.AttributeSet
import android.view.LayoutInflater
-import android.view.View
import android.view.inputmethod.EditorInfo
+import androidx.core.content.withStyledAttributes
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import androidx.transition.AutoTransition
import androidx.transition.TransitionManager
@@ -55,9 +55,9 @@ class PinEntryCardView(
@Inject
lateinit var verificationMethods: Map
- val downstreamUiEvents: PublishSubject = PublishSubject.create()
+ val downstreamUiEvents: PublishSubject = PublishSubject.create()
- private val method: Method
+ private lateinit var method: Method
private var binding: PinEntryCardBinding? = null
@@ -89,15 +89,14 @@ class PinEntryCardView(
setPinEntryMode(PinEntryUi.Mode.PinEntry)
setForgotButtonVisible(true)
- with(context.obtainStyledAttributes(attrs, R.styleable.PinEntryCardView)) {
+ context.withStyledAttributes(attrs, R.styleable.PinEntryCardView) {
val methodIndex = getInt(R.styleable.PinEntryCardView_verificationMethod, -1)
if (methodIndex < 0) {
throw RuntimeException("No verification method defined!")
}
- method = Method.values()[methodIndex]
+ method = Method.entries.toTypedArray()[methodIndex]
- recycle()
}
}
@@ -236,12 +235,12 @@ class PinEntryCardView(
fun showError(error: String) {
errorTextView.text = error
- errorTextView.visibility = View.VISIBLE
+ errorTextView.visibility = VISIBLE
clearPin()
}
override fun hideError() {
- errorTextView.visibility = View.GONE
+ errorTextView.visibility = GONE
}
override fun showIncorrectPinErrorForFirstAttempt() {
@@ -281,11 +280,11 @@ class PinEntryCardView(
/** Defaults to visible. */
fun setForgotButtonVisible(visible: Boolean) {
if (visible) {
- forgotPinButton.visibility = View.VISIBLE
+ forgotPinButton.visibility = VISIBLE
contentContainer.setPaddingBottom(R.dimen.pinentry_content_bottom_spacing_with_forgot_pin)
} else {
- forgotPinButton.visibility = View.GONE
+ forgotPinButton.visibility = GONE
contentContainer.setPaddingBottom(R.dimen.pinentry_content_bottom_spacing_without_forgot_pin)
}
}
diff --git a/app/src/main/java/org/simple/clinic/settings/SettingsScreen.kt b/app/src/main/java/org/simple/clinic/settings/SettingsScreen.kt
index 2ef3048189a..7a4c065db70 100644
--- a/app/src/main/java/org/simple/clinic/settings/SettingsScreen.kt
+++ b/app/src/main/java/org/simple/clinic/settings/SettingsScreen.kt
@@ -2,7 +2,6 @@ package org.simple.clinic.settings
import android.content.Context
import android.content.Intent
-import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -11,6 +10,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
+import androidx.core.net.toUri
import androidx.fragment.app.Fragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.parcelize.Parcelize
@@ -122,7 +122,7 @@ class SettingsScreen : Fragment(), UiActions, HandlesBack {
private fun launchPlayStoreForUpdate() {
val intent = Intent(Intent.ACTION_VIEW).apply {
- data = Uri.parse(PLAY_STORE_URL_FOR_SIMPLE)
+ data = PLAY_STORE_URL_FOR_SIMPLE.toUri()
}
requireContext().startActivity(intent)
}
diff --git a/app/src/main/java/org/simple/clinic/storage/migrations/Migration_121.kt b/app/src/main/java/org/simple/clinic/storage/migrations/Migration_121.kt
new file mode 100644
index 00000000000..cddf98d76f3
--- /dev/null
+++ b/app/src/main/java/org/simple/clinic/storage/migrations/Migration_121.kt
@@ -0,0 +1,19 @@
+package org.simple.clinic.storage.migrations
+
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+import org.simple.clinic.storage.inTransaction
+import javax.inject.Inject
+
+@Suppress("ClassName")
+class Migration_121 @Inject constructor() : Migration(120, 121) {
+
+ override fun migrate(db: SupportSQLiteDatabase) {
+ db.inTransaction {
+ execSQL("""
+ ALTER TABLE "MedicalHistory"
+ ADD COLUMN "isUsingSmokelessTobacco" TEXT NOT NULL DEFAULT "unknown"
+ """.trimIndent())
+ }
+ }
+}
diff --git a/app/src/main/java/org/simple/clinic/storage/migrations/RoomMigrationsModule.kt b/app/src/main/java/org/simple/clinic/storage/migrations/RoomMigrationsModule.kt
index 83e6a2e4526..ffe427de045 100644
--- a/app/src/main/java/org/simple/clinic/storage/migrations/RoomMigrationsModule.kt
+++ b/app/src/main/java/org/simple/clinic/storage/migrations/RoomMigrationsModule.kt
@@ -126,6 +126,7 @@ class RoomMigrationsModule {
migration118: Migration_118,
migration119: Migration_119,
migration120: Migration_120,
+ migration121: Migration_121,
): List {
return listOf(
migration_3_4,
@@ -245,6 +246,7 @@ class RoomMigrationsModule {
migration118,
migration119,
migration120,
+ migration121,
)
}
}
diff --git a/app/src/main/java/org/simple/clinic/summary/PatientSummaryEffect.kt b/app/src/main/java/org/simple/clinic/summary/PatientSummaryEffect.kt
index bd8ce524d3f..cde8485dfa7 100644
--- a/app/src/main/java/org/simple/clinic/summary/PatientSummaryEffect.kt
+++ b/app/src/main/java/org/simple/clinic/summary/PatientSummaryEffect.kt
@@ -77,7 +77,11 @@ data class UpdateCVDRisk(
data class LoadStatinInfo(val patientUuid: UUID) : PatientSummaryEffect()
-data class UpdateSmokingStatus(val patientId: UUID, val isSmoker: MedicalHistoryAnswer) : PatientSummaryEffect()
+data class UpdateTobaccoUse(
+ val patientId: UUID,
+ val isSmoker: MedicalHistoryAnswer,
+ val isUsingSmokelessTobacco: MedicalHistoryAnswer
+) : PatientSummaryEffect()
sealed class PatientSummaryViewEffect : PatientSummaryEffect()
diff --git a/app/src/main/java/org/simple/clinic/summary/PatientSummaryEffectHandler.kt b/app/src/main/java/org/simple/clinic/summary/PatientSummaryEffectHandler.kt
index 27e36405322..9b2018806de 100644
--- a/app/src/main/java/org/simple/clinic/summary/PatientSummaryEffectHandler.kt
+++ b/app/src/main/java/org/simple/clinic/summary/PatientSummaryEffectHandler.kt
@@ -111,7 +111,7 @@ class PatientSummaryEffectHandler @AssistedInject constructor(
.addTransformer(UpdateCVDRisk::class.java, updateCVDRisk())
.addTransformer(SaveCVDRisk::class.java, saveCVDRisk())
.addTransformer(LoadStatinInfo::class.java, loadStatinInfo())
- .addConsumer(UpdateSmokingStatus::class.java, { updateSmokingStatus(it.patientId, it.isSmoker) }, schedulersProvider.io())
+ .addConsumer(UpdateTobaccoUse::class.java, { updateTobaccoUse(it.patientId, it.isSmoker, it.isUsingSmokelessTobacco) }, schedulersProvider.io())
.build()
}
@@ -302,7 +302,11 @@ class PatientSummaryEffectHandler @AssistedInject constructor(
}
}
- private fun updateSmokingStatus(patientUuid: UUID, isSmoker: MedicalHistoryAnswer) {
+ private fun updateTobaccoUse(
+ patientUuid: UUID,
+ isSmoker: MedicalHistoryAnswer,
+ isUsingSmokelessTobacco: MedicalHistoryAnswer
+ ) {
val medicalHistory = medicalHistoryRepository.historyForPatientOrDefaultImmediate(
patientUuid = patientUuid,
defaultHistoryUuid = uuidGenerator.v4()
@@ -310,6 +314,9 @@ class PatientSummaryEffectHandler @AssistedInject constructor(
val updatedMedicalHistory = medicalHistory.answered(
question = MedicalHistoryQuestion.IsSmoking,
answer = isSmoker
+ ).answered(
+ question = MedicalHistoryQuestion.IsUsingSmokelessTobacco,
+ answer = isUsingSmokelessTobacco
)
medicalHistoryRepository.save(updatedMedicalHistory, Instant.now(clock))
diff --git a/app/src/main/java/org/simple/clinic/summary/PatientSummaryEvent.kt b/app/src/main/java/org/simple/clinic/summary/PatientSummaryEvent.kt
index 6262cbe359e..cbc3630c5f4 100644
--- a/app/src/main/java/org/simple/clinic/summary/PatientSummaryEvent.kt
+++ b/app/src/main/java/org/simple/clinic/summary/PatientSummaryEvent.kt
@@ -172,8 +172,9 @@ data class StatinInfoLoaded(
data object AddTobaccoUseClicked : PatientSummaryEvent()
-data class SmokingStatusAnswered(
- val isSmoker: Answer
+data class TobaccoUseAnswered(
+ val isSmoker: Answer,
+ val isUsingSmokelessTobacco: Answer = Answer.Unanswered
) : PatientSummaryEvent()
data object BMIReadingAdded : PatientSummaryEvent()
diff --git a/app/src/main/java/org/simple/clinic/summary/PatientSummaryScreen.kt b/app/src/main/java/org/simple/clinic/summary/PatientSummaryScreen.kt
index ec50dc44c4b..f7b02e79c1f 100644
--- a/app/src/main/java/org/simple/clinic/summary/PatientSummaryScreen.kt
+++ b/app/src/main/java/org/simple/clinic/summary/PatientSummaryScreen.kt
@@ -692,18 +692,19 @@ class PatientSummaryScreen :
}
override fun showTobaccoStatusDialog() {
- val options = if (country.isoCountryCode == Country.ETHIOPIA) {
- arrayOf(
- getString(R.string.tobacco_status_dialog_option_smokes),
- getString(R.string.tobacco_status_dialog_option_no)
- )
+ if (country.isoCountryCode == Country.ETHIOPIA) {
+ showTobaccoUseDialogWithoutSmokelessTobacco()
} else {
- arrayOf(
- getString(R.string.tobacco_status_dialog_option_smokes),
- getString(R.string.tobacco_status_dialog_option_smokeless),
- getString(R.string.tobacco_status_dialog_option_no)
- )
+ showTobaccoUseDialogWithSmokelessTobacco()
}
+ }
+
+ fun showTobaccoUseDialogWithSmokelessTobacco() {
+ val options = arrayOf(
+ getString(R.string.tobacco_status_dialog_option_smokes),
+ getString(R.string.tobacco_status_dialog_option_smokeless),
+ getString(R.string.tobacco_status_dialog_option_no)
+ )
val checkedItems = BooleanArray(options.size)
@@ -713,10 +714,36 @@ class PatientSummaryScreen :
checkedItems[index] = isChecked
}
.setPositiveButton(R.string.tobacco_status_dialog_title_positive_button) { _, _ ->
- if (checkedItems[0]) {
- hotEvents.onNext(SmokingStatusAnswered(Answer.Yes))
- } else {
- hotEvents.onNext(SmokingStatusAnswered(Answer.No))
+ val answeredNo = checkedItems[2]
+ val isSmoker = if (checkedItems[0] && !answeredNo) Answer.Yes else Answer.No
+ val isUsingSmokeless = if (checkedItems[1] && !answeredNo) Answer.Yes else Answer.No
+
+ hotEvents.onNext(TobaccoUseAnswered(
+ isSmoker = isSmoker,
+ isUsingSmokelessTobacco = isUsingSmokeless
+ ))
+ }
+ .setNegativeButton(R.string.tobacco_status_dialog_title_negative_button, null)
+ .show()
+ }
+
+ fun showTobaccoUseDialogWithoutSmokelessTobacco() {
+ val options = arrayOf(
+ getString(R.string.tobacco_status_dialog_option_smokes),
+ getString(R.string.tobacco_status_dialog_option_no))
+
+ var selectedOption = 1
+
+ MaterialAlertDialogBuilder(requireContext(), R.style.ThemeOverlay_Simple_MaterialAlertDialog_CheckedItem)
+ .setTitle(R.string.tobacco_status_dialog_title)
+ .setSingleChoiceItems(options, selectedOption) { _, indexSelected ->
+ selectedOption = indexSelected
+ }
+ .setPositiveButton(R.string.tobacco_status_dialog_title_positive_button) { _, _ ->
+ when (selectedOption) {
+ 0 -> hotEvents.onNext(TobaccoUseAnswered(Answer.Yes))
+ 1 -> hotEvents.onNext(TobaccoUseAnswered(Answer.No))
+ else -> {}
}
}
.setNegativeButton(R.string.tobacco_status_dialog_title_negative_button, null)
diff --git a/app/src/main/java/org/simple/clinic/summary/PatientSummaryUpdate.kt b/app/src/main/java/org/simple/clinic/summary/PatientSummaryUpdate.kt
index 66cf53901ad..60bbe8295b9 100644
--- a/app/src/main/java/org/simple/clinic/summary/PatientSummaryUpdate.kt
+++ b/app/src/main/java/org/simple/clinic/summary/PatientSummaryUpdate.kt
@@ -106,7 +106,7 @@ class PatientSummaryUpdate(
is CVDRiskUpdated -> dispatch(LoadStatinInfo(model.patientUuid))
is StatinInfoLoaded -> statinInfoLoaded(event, model)
is AddTobaccoUseClicked -> dispatch(ShowTobaccoStatusDialog)
- is SmokingStatusAnswered -> dispatch(UpdateSmokingStatus(model.patientUuid, event.isSmoker))
+ is TobaccoUseAnswered -> dispatch(UpdateTobaccoUse(model.patientUuid, event.isSmoker, event.isUsingSmokelessTobacco))
is BMIReadingAdded -> dispatch(CalculateNonLabBasedCVDRisk(model.patientSummaryProfile!!.patient))
is AddBMIClicked -> dispatch(OpenBMIEntrySheet(model.patientUuid))
is AddCholesterolClicked -> dispatch(OpenCholesterolEntrySheet(model.patientUuid))
diff --git a/app/src/main/java/org/simple/clinic/summary/medicalhistory/MedicalHistorySummaryModel.kt b/app/src/main/java/org/simple/clinic/summary/medicalhistory/MedicalHistorySummaryModel.kt
index a3c375adc97..6fe53b58fab 100644
--- a/app/src/main/java/org/simple/clinic/summary/medicalhistory/MedicalHistorySummaryModel.kt
+++ b/app/src/main/java/org/simple/clinic/summary/medicalhistory/MedicalHistorySummaryModel.kt
@@ -15,12 +15,18 @@ data class MedicalHistorySummaryModel(
val medicalHistory: MedicalHistory? = null,
val currentFacility: Facility? = null,
val showIsSmokingQuestion: Boolean,
+ val showSmokelessTobaccoQuestion: Boolean,
) : Parcelable, PatientSummaryChildModel {
companion object {
- fun create(patientUuid: UUID, showIsSmokingQuestion: Boolean): MedicalHistorySummaryModel = MedicalHistorySummaryModel(
+ fun create(
+ patientUuid: UUID,
+ showIsSmokingQuestion: Boolean,
+ showSmokelessTobaccoQuestion: Boolean,
+ ): MedicalHistorySummaryModel = MedicalHistorySummaryModel(
patientUuid = patientUuid,
- showIsSmokingQuestion = showIsSmokingQuestion
+ showIsSmokingQuestion = showIsSmokingQuestion,
+ showSmokelessTobaccoQuestion = showSmokelessTobaccoQuestion
)
}
diff --git a/app/src/main/java/org/simple/clinic/summary/medicalhistory/MedicalHistorySummaryUi.kt b/app/src/main/java/org/simple/clinic/summary/medicalhistory/MedicalHistorySummaryUi.kt
index a43c10f450d..87d2e03f3cc 100644
--- a/app/src/main/java/org/simple/clinic/summary/medicalhistory/MedicalHistorySummaryUi.kt
+++ b/app/src/main/java/org/simple/clinic/summary/medicalhistory/MedicalHistorySummaryUi.kt
@@ -8,4 +8,7 @@ interface MedicalHistorySummaryUi {
fun hideDiagnosisView()
fun showCurrentSmokerQuestion()
fun hideCurrentSmokerQuestion()
+
+ fun showSmokelessTobaccoQuestion()
+ fun hideSmokelessTobaccoQuestion()
}
diff --git a/app/src/main/java/org/simple/clinic/summary/medicalhistory/MedicalHistorySummaryUiRenderer.kt b/app/src/main/java/org/simple/clinic/summary/medicalhistory/MedicalHistorySummaryUiRenderer.kt
index 5128d2c4ad8..0e377d26e63 100644
--- a/app/src/main/java/org/simple/clinic/summary/medicalhistory/MedicalHistorySummaryUiRenderer.kt
+++ b/app/src/main/java/org/simple/clinic/summary/medicalhistory/MedicalHistorySummaryUiRenderer.kt
@@ -14,6 +14,8 @@ class MedicalHistorySummaryUiRenderer(
private val smokerQuestionVisibilityChangedCallback = ValueChangedCallback()
+ private val smokelessTobaccoQuestionVisibilityChangedCallback = ValueChangedCallback()
+
override fun render(model: MedicalHistorySummaryModel) {
if (model.hasLoadedMedicalHistory) {
medicalHistoryChangedCallback.pass(model.medicalHistory!!, ui::populateMedicalHistory)
@@ -30,6 +32,14 @@ class MedicalHistorySummaryUiRenderer(
ui.hideCurrentSmokerQuestion()
}
}
+
+ smokelessTobaccoQuestionVisibilityChangedCallback.pass(model.showSmokelessTobaccoQuestion) { show ->
+ if (show) {
+ ui.showSmokelessTobaccoQuestion()
+ } else {
+ ui.hideSmokelessTobaccoQuestion()
+ }
+ }
}
private fun toggleDiabetesManagementUi(diabetesManagementEnabled: Boolean) {
diff --git a/app/src/main/java/org/simple/clinic/summary/medicalhistory/MedicalHistorySummaryView.kt b/app/src/main/java/org/simple/clinic/summary/medicalhistory/MedicalHistorySummaryView.kt
index 9046f7f0eee..9a4af3c8a29 100644
--- a/app/src/main/java/org/simple/clinic/summary/medicalhistory/MedicalHistorySummaryView.kt
+++ b/app/src/main/java/org/simple/clinic/summary/medicalhistory/MedicalHistorySummaryView.kt
@@ -15,6 +15,7 @@ import io.reactivex.Observable
import io.reactivex.rxkotlin.ofType
import io.reactivex.subjects.PublishSubject
import org.simple.clinic.ReportAnalyticsEvents
+import org.simple.clinic.appconfig.Country
import org.simple.clinic.common.ui.theme.SimpleTheme
import org.simple.clinic.di.injector
import org.simple.clinic.feature.Feature
@@ -44,6 +45,7 @@ class MedicalHistorySummaryView(
private var medicalHistory by mutableStateOf(null)
private var diabetesManagementEnabled by mutableStateOf(false)
private var showSmokerQuestion by mutableStateOf(false)
+ private var showSmokelessTobaccoQuestion by mutableStateOf(false)
@Inject
lateinit var activity: AppCompatActivity
@@ -57,6 +59,9 @@ class MedicalHistorySummaryView(
@Inject
lateinit var features: Features
+ @Inject
+ lateinit var country: Country
+
private var modelUpdateCallback: PatientSummaryModelUpdateCallback? = null
private val screenKey by unsafeLazy { screenKeyProvider.keyFor(this) }
@@ -78,7 +83,8 @@ class MedicalHistorySummaryView(
defaultModel = MedicalHistorySummaryModel.create(
patientUuid = screenKey.patientUuid,
showIsSmokingQuestion = features.isEnabled(Feature.NonLabBasedStatinNudge) ||
- features.isEnabled(Feature.LabBasedStatinNudge)
+ features.isEnabled(Feature.LabBasedStatinNudge),
+ showSmokelessTobaccoQuestion = country.isoCountryCode != Country.ETHIOPIA
),
update = MedicalHistorySummaryUpdate(),
init = MedicalHistorySummaryInit(),
@@ -112,9 +118,11 @@ class MedicalHistorySummaryView(
heartAttackAnswer = medicalHistory?.hasHadHeartAttack,
strokeAnswer = medicalHistory?.hasHadStroke,
kidneyAnswer = medicalHistory?.hasHadKidneyDisease,
- smokerAnswer = medicalHistory?.isSmoking,
+ isSmokingAnswer = medicalHistory?.isSmoking,
+ isUsingSmokelessTobaccoAnswer = medicalHistory?.isUsingSmokelessTobacco,
diabetesManagementEnabled = diabetesManagementEnabled,
showSmokerQuestion = showSmokerQuestion,
+ showSmokelessTobaccoQuestion = showSmokelessTobaccoQuestion
) { question, answer ->
answerToggled(question, answer)
}
@@ -164,6 +172,14 @@ class MedicalHistorySummaryView(
showSmokerQuestion = false
}
+ override fun showSmokelessTobaccoQuestion() {
+ showSmokelessTobaccoQuestion = true
+ }
+
+ override fun hideSmokelessTobaccoQuestion() {
+ showSmokelessTobaccoQuestion = false
+ }
+
override fun registerSummaryModelUpdateCallback(callback: PatientSummaryModelUpdateCallback?) {
modelUpdateCallback = callback
}
diff --git a/app/src/main/java/org/simple/clinic/summary/medicalhistory/ui/MedicalHistorySummary.kt b/app/src/main/java/org/simple/clinic/summary/medicalhistory/ui/MedicalHistorySummary.kt
index bce102cc6ae..cb21c83b511 100644
--- a/app/src/main/java/org/simple/clinic/summary/medicalhistory/ui/MedicalHistorySummary.kt
+++ b/app/src/main/java/org/simple/clinic/summary/medicalhistory/ui/MedicalHistorySummary.kt
@@ -19,6 +19,7 @@ import org.simple.clinic.common.ui.theme.SimpleTheme
import org.simple.clinic.medicalhistory.Answer
import org.simple.clinic.medicalhistory.MedicalHistoryQuestion
import org.simple.clinic.medicalhistory.ui.MedicalHistoryQuestionItem
+import org.simple.clinic.medicalhistory.ui.TobaccoQuestion
@Composable
fun MedicalHistorySummary(
@@ -27,9 +28,11 @@ fun MedicalHistorySummary(
heartAttackAnswer: Answer?,
strokeAnswer: Answer?,
kidneyAnswer: Answer?,
- smokerAnswer: Answer?,
+ isSmokingAnswer: Answer?,
+ isUsingSmokelessTobaccoAnswer: Answer?,
diabetesManagementEnabled: Boolean,
showSmokerQuestion: Boolean,
+ showSmokelessTobaccoQuestion: Boolean,
modifier: Modifier = Modifier,
onAnswerChange: (MedicalHistoryQuestion, Answer) -> Unit,
) {
@@ -58,35 +61,16 @@ fun MedicalHistorySummary(
)
if (showSmokerQuestion) {
- SmokerContainer(
- smokerAnswer = smokerAnswer,
+ TobaccoQuestion(
+ isSmokingAnswer = isSmokingAnswer,
+ isUsingSmokelessTobaccoAnswer = isUsingSmokelessTobaccoAnswer,
+ showSmokelessTobaccoQuestion = showSmokelessTobaccoQuestion,
onAnswerChange = onAnswerChange
)
}
}
}
-@Composable
-fun SmokerContainer(
- smokerAnswer: Answer?,
- modifier: Modifier = Modifier,
- onAnswerChange: (MedicalHistoryQuestion, Answer) -> Unit,
-) {
- Card(modifier = modifier) {
- MedicalHistoryQuestionItem(
- modifier = Modifier
- .padding(horizontal = dimensionResource(R.dimen.spacing_16))
- .padding(vertical = dimensionResource(R.dimen.spacing_8)),
- question = MedicalHistoryQuestion.IsSmoking,
- selectedAnswer = smokerAnswer,
- showDivider = false,
- onSelectionChange = {
- onAnswerChange(MedicalHistoryQuestion.IsSmoking, it)
- }
- )
- }
-}
-
@Composable
fun HistoryContainer(
heartAttackAnswer: Answer?,
@@ -204,9 +188,11 @@ private fun MedicalHistorySummaryPreview() {
heartAttackAnswer = Answer.Yes,
strokeAnswer = Answer.No,
kidneyAnswer = null,
- smokerAnswer = null,
+ isSmokingAnswer = null,
+ isUsingSmokelessTobaccoAnswer = null,
diabetesManagementEnabled = true,
showSmokerQuestion = false,
+ showSmokelessTobaccoQuestion = false,
onAnswerChange = { _, _ ->
// no-op
}
@@ -224,9 +210,11 @@ private fun MedicalHistorySummaryNoDiabetesManagementPreview() {
heartAttackAnswer = Answer.Yes,
strokeAnswer = Answer.No,
kidneyAnswer = null,
- smokerAnswer = null,
+ isSmokingAnswer = null,
+ isUsingSmokelessTobaccoAnswer = null,
diabetesManagementEnabled = false,
showSmokerQuestion = false,
+ showSmokelessTobaccoQuestion = false,
onAnswerChange = { _, _ ->
// no-op
}
@@ -244,9 +232,33 @@ private fun MedicalHistorySummarySmokerPreview() {
heartAttackAnswer = Answer.Yes,
strokeAnswer = Answer.No,
kidneyAnswer = null,
- smokerAnswer = Answer.Yes,
+ isSmokingAnswer = Answer.Yes,
+ isUsingSmokelessTobaccoAnswer = Answer.Yes,
+ diabetesManagementEnabled = true,
+ showSmokerQuestion = true,
+ showSmokelessTobaccoQuestion = false,
+ onAnswerChange = { _, _ ->
+ // no-op
+ }
+ )
+ }
+}
+
+@Preview
+@Composable
+private fun MedicalHistorySummaryTobaccoUsePreview() {
+ SimpleTheme {
+ MedicalHistorySummary(
+ hypertensionAnswer = Answer.Yes,
+ diabetesAnswer = null,
+ heartAttackAnswer = Answer.Yes,
+ strokeAnswer = Answer.No,
+ kidneyAnswer = null,
+ isSmokingAnswer = Answer.Yes,
+ isUsingSmokelessTobaccoAnswer = Answer.Yes,
diabetesManagementEnabled = true,
showSmokerQuestion = true,
+ showSmokelessTobaccoQuestion = true,
onAnswerChange = { _, _ ->
// no-op
}
diff --git a/app/src/main/java/org/simple/clinic/teleconsultlog/shareprescription/TeleconsultSharePrescriptionScreen.kt b/app/src/main/java/org/simple/clinic/teleconsultlog/shareprescription/TeleconsultSharePrescriptionScreen.kt
index 70b366a3e0e..02adccc9ad2 100644
--- a/app/src/main/java/org/simple/clinic/teleconsultlog/shareprescription/TeleconsultSharePrescriptionScreen.kt
+++ b/app/src/main/java/org/simple/clinic/teleconsultlog/shareprescription/TeleconsultSharePrescriptionScreen.kt
@@ -12,6 +12,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
+import androidx.core.graphics.createBitmap
import com.jakewharton.rxbinding3.appcompat.navigationClicks
import com.jakewharton.rxbinding3.view.clicks
import com.spotify.mobius.functions.Consumer
@@ -21,6 +22,8 @@ import io.reactivex.rxkotlin.ofType
import io.reactivex.subjects.PublishSubject
import org.simple.clinic.R
import org.simple.clinic.ReportAnalyticsEvents
+import org.simple.clinic.activity.permissions.RequestPermissions
+import org.simple.clinic.activity.permissions.RuntimePermissions
import org.simple.clinic.databinding.ListItemTeleconsultSharePrescriptionMedicineBinding
import org.simple.clinic.databinding.ScreenTeleconsultSharePrescriptionBinding
import org.simple.clinic.di.injector
@@ -28,13 +31,11 @@ import org.simple.clinic.drugs.PrescribedDrug
import org.simple.clinic.home.HomeScreenKey
import org.simple.clinic.mobius.ViewRenderer
import org.simple.clinic.navigation.v2.Router
+import org.simple.clinic.navigation.v2.ScreenResultBus
import org.simple.clinic.navigation.v2.fragments.BaseScreen
import org.simple.clinic.patient.PatientProfile
import org.simple.clinic.patient.displayLetterRes
-import org.simple.clinic.navigation.v2.ScreenResultBus
import org.simple.clinic.teleconsultlog.prescription.medicines.TeleconsultMedicinesConfig
-import org.simple.clinic.activity.permissions.RequestPermissions
-import org.simple.clinic.activity.permissions.RuntimePermissions
import org.simple.clinic.util.UserClock
import org.simple.clinic.util.applyInsetsBottomPadding
import org.simple.clinic.util.applyStatusBarPadding
@@ -306,7 +307,7 @@ class TeleconsultSharePrescriptionScreen :
val bitmapWidth = (sourceWidth * scaleFactor).toInt()
val bitmapHeight = (sourceHeight * scaleFactor).toInt()
- val bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888)
+ val bitmap = createBitmap(bitmapWidth, bitmapHeight)
val canvas = Canvas(bitmap).apply {
drawColor(Color.WHITE)
}
diff --git a/app/src/main/java/org/simple/clinic/text/style/TextAppearanceWithLetterSpacingSpan.kt b/app/src/main/java/org/simple/clinic/text/style/TextAppearanceWithLetterSpacingSpan.kt
index 446c999c738..a06dd87d445 100644
--- a/app/src/main/java/org/simple/clinic/text/style/TextAppearanceWithLetterSpacingSpan.kt
+++ b/app/src/main/java/org/simple/clinic/text/style/TextAppearanceWithLetterSpacingSpan.kt
@@ -6,6 +6,7 @@ import android.text.Spanned
import android.text.TextPaint
import android.text.style.TextAppearanceSpan
import androidx.annotation.StyleRes
+import androidx.core.content.withStyledAttributes
/**
* The platform [TextAppearanceSpan] does not support setting letter spacing for the spanned text.
@@ -24,12 +25,12 @@ class TextAppearanceWithLetterSpacingSpan(
@StyleRes textAppearanceResId: Int
) : TextAppearanceSpan(context, textAppearanceResId) {
- private val letterSpacing: Float
+ private var letterSpacing: Float = 0.0f
init {
- val typedArray = context.obtainStyledAttributes(textAppearanceResId, intArrayOf(android.R.attr.letterSpacing))
- letterSpacing = typedArray.getFloat(0, 0F)
- typedArray.recycle()
+ context.withStyledAttributes(textAppearanceResId, intArrayOf(android.R.attr.letterSpacing)) {
+ letterSpacing = getFloat(0, 0F)
+ }
}
override fun updateMeasureState(ds: TextPaint) {
diff --git a/app/src/main/java/org/simple/clinic/user/UserSession.kt b/app/src/main/java/org/simple/clinic/user/UserSession.kt
index e36517e0d29..a47fe3f6071 100644
--- a/app/src/main/java/org/simple/clinic/user/UserSession.kt
+++ b/app/src/main/java/org/simple/clinic/user/UserSession.kt
@@ -3,6 +3,7 @@ package org.simple.clinic.user
import android.content.SharedPreferences
import android.os.Parcelable
import androidx.annotation.WorkerThread
+import androidx.core.content.edit
import com.f2prateek.rx.preferences2.Preference
import io.reactivex.Completable
import io.reactivex.Observable
@@ -15,7 +16,6 @@ import org.simple.clinic.main.TypedPreference
import org.simple.clinic.main.TypedPreference.Type.OnboardingComplete
import org.simple.clinic.platform.analytics.Analytics
import org.simple.clinic.plumbing.infrastructure.Infrastructure
-import org.simple.clinic.plumbing.infrastructure.UpdateInfrastructureUserDetails
import org.simple.clinic.security.PasswordHasher
import org.simple.clinic.storage.SharedPreferencesMode
import org.simple.clinic.storage.SharedPreferencesMode.Mode.Default
@@ -126,14 +126,14 @@ class UserSession @Inject constructor(
// Retain the saved country when clearing the shared preferences
val savedCountryData = sharedPreferences.getString(selectedCountryPreference.key(), "")
- sharedPreferences.edit().clear().apply()
+ sharedPreferences.edit { clear() }
// When we clear all shared preferences, we also end up clearing the flag that states whether
// the user has completed the onboarding flow or not. This means that if the user opens the
// again after getting logged out and before logging in, they will be shown the Onboarding
// screen instead of the Registration phone screen. This is a workaround that sets the flag
// again after clearing the shared preferences to fix this.
onboardingComplete.set(true)
- sharedPreferences.edit().putString(selectedCountryPreference.key(), savedCountryData).apply()
+ sharedPreferences.edit { putString(selectedCountryPreference.key(), savedCountryData) }
}
}
diff --git a/app/src/main/java/org/simple/clinic/util/AndroidExtensions.kt b/app/src/main/java/org/simple/clinic/util/AndroidExtensions.kt
index 8ef294b3b8e..c7d74564ac9 100644
--- a/app/src/main/java/org/simple/clinic/util/AndroidExtensions.kt
+++ b/app/src/main/java/org/simple/clinic/util/AndroidExtensions.kt
@@ -5,7 +5,6 @@ import android.app.Dialog
import android.content.Context
import android.content.Intent
import android.content.res.Configuration
-import android.os.Build
import android.view.KeyEvent
import androidx.annotation.AttrRes
import androidx.annotation.ColorInt
@@ -63,8 +62,7 @@ fun Configuration.withLocale(
private fun Configuration.isLocaleAlreadyOverriden(): Boolean {
return when {
- Build.VERSION.SDK_INT >= 24 && !this.locales.isEmpty -> true
- Build.VERSION.SDK_INT < 24 && this.locale != null -> true
+ !this.locales.isEmpty -> true
else -> false
}
}
diff --git a/app/src/main/java/org/simple/clinic/util/AppSignature.kt b/app/src/main/java/org/simple/clinic/util/AppSignature.kt
index f59c8160a46..9b468e2a75c 100644
--- a/app/src/main/java/org/simple/clinic/util/AppSignature.kt
+++ b/app/src/main/java/org/simple/clinic/util/AppSignature.kt
@@ -1,15 +1,14 @@
package org.simple.clinic.util
import android.annotation.SuppressLint
-import android.annotation.TargetApi
import android.content.Context
import android.content.pm.PackageManager
import android.content.pm.Signature
import android.os.Build
import android.util.Base64
+import androidx.annotation.RequiresApi
import java.nio.charset.StandardCharsets
import java.security.MessageDigest
-import java.util.Arrays
private const val HASH_TYPE = "SHA-256"
private const val NUM_HASHED_BYTES = 9
@@ -45,7 +44,7 @@ class AppSignature(private val context: Context) {
}
}
- @TargetApi(Build.VERSION_CODES.P)
+ @RequiresApi(Build.VERSION_CODES.P)
private fun signaturesV28(packageManager: PackageManager, packageName: String): List {
val signingInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNING_CERTIFICATES).signingInfo ?: return emptyList()
@@ -67,7 +66,7 @@ class AppSignature(private val context: Context) {
var hashSignature = messageDigest.digest()
// Truncated into NUM_HASHED_BYTES
- hashSignature = Arrays.copyOfRange(hashSignature, 0, NUM_HASHED_BYTES)
+ hashSignature = hashSignature.copyOfRange(0, NUM_HASHED_BYTES)
val base64Hash = Base64.encodeToString(hashSignature, Base64.NO_PADDING or Base64.NO_WRAP)
diff --git a/app/src/main/java/org/simple/clinic/util/Strings.kt b/app/src/main/java/org/simple/clinic/util/Strings.kt
index e9fba9c7917..40dee8ab80b 100644
--- a/app/src/main/java/org/simple/clinic/util/Strings.kt
+++ b/app/src/main/java/org/simple/clinic/util/Strings.kt
@@ -1,7 +1,6 @@
package org.simple.clinic.util
import android.graphics.Typeface
-import android.os.Build
import android.text.Html
import android.text.Spanned
import android.text.style.StyleSpan
@@ -42,9 +41,5 @@ private fun Spanned.toAnnotatedString(): AnnotatedString = buildAnnotatedString
}
private fun getSpannedText(text: String): Spanned {
- return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- Html.fromHtml(text, Html.FROM_HTML_MODE_COMPACT)
- } else {
- Html.fromHtml(text)
- }
+ return Html.fromHtml(text, Html.FROM_HTML_MODE_COMPACT)
}
diff --git a/app/src/main/java/org/simple/clinic/util/Vibrator.kt b/app/src/main/java/org/simple/clinic/util/Vibrator.kt
index 165e20f1719..9f32c72d89a 100644
--- a/app/src/main/java/org/simple/clinic/util/Vibrator.kt
+++ b/app/src/main/java/org/simple/clinic/util/Vibrator.kt
@@ -1,6 +1,5 @@
package org.simple.clinic.util
-import android.os.Build
import android.os.VibrationEffect
import android.os.Vibrator
import javax.inject.Inject
@@ -9,11 +8,7 @@ class Vibrator @Inject constructor(private val vibrator: Vibrator) {
@Suppress("DEPRECATION")
fun vibrate(millis: Long) {
- if (Build.VERSION.SDK_INT >= 26) {
- vibrator.vibrate(VibrationEffect.createOneShot(millis, VibrationEffect.DEFAULT_AMPLITUDE))
- } else {
- vibrator.vibrate(millis)
- }
+ vibrator.vibrate(VibrationEffect.createOneShot(millis, VibrationEffect.DEFAULT_AMPLITUDE))
}
}
diff --git a/app/src/main/java/org/simple/clinic/util/messagesender/SmsMessageSender.kt b/app/src/main/java/org/simple/clinic/util/messagesender/SmsMessageSender.kt
index 59604dab442..5aa8baeca55 100644
--- a/app/src/main/java/org/simple/clinic/util/messagesender/SmsMessageSender.kt
+++ b/app/src/main/java/org/simple/clinic/util/messagesender/SmsMessageSender.kt
@@ -1,8 +1,8 @@
package org.simple.clinic.util.messagesender
import android.content.Intent
-import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
+import androidx.core.net.toUri
import javax.inject.Inject
class SmsMessageSender @Inject constructor(
@@ -10,7 +10,7 @@ class SmsMessageSender @Inject constructor(
) : MessageSender {
override fun send(phoneNumber: String, message: String) {
- val uri = Uri.parse("smsto:$phoneNumber")
+ val uri = "smsto:$phoneNumber".toUri()
val intent = Intent(Intent.ACTION_SENDTO, uri).apply {
putExtra("sms_body", message)
}
diff --git a/app/src/main/java/org/simple/clinic/util/messagesender/WhatsAppMessageSender.kt b/app/src/main/java/org/simple/clinic/util/messagesender/WhatsAppMessageSender.kt
index e3259ca0a59..f7fbcf0f70c 100644
--- a/app/src/main/java/org/simple/clinic/util/messagesender/WhatsAppMessageSender.kt
+++ b/app/src/main/java/org/simple/clinic/util/messagesender/WhatsAppMessageSender.kt
@@ -1,8 +1,8 @@
package org.simple.clinic.util.messagesender
import android.content.Intent
-import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
+import androidx.core.net.toUri
import java.net.URLEncoder
import javax.inject.Inject
@@ -15,7 +15,7 @@ class WhatsAppMessageSender @Inject constructor(
val encodedMessage = URLEncoder.encode(message, "UTF-8")
val intent = Intent().apply {
action = Intent.ACTION_VIEW
- data = Uri.parse("https://wa.me/$whatsAppPhoneNumber?text=$encodedMessage")
+ data = "https://wa.me/$whatsAppPhoneNumber?text=$encodedMessage".toUri()
}
activity.startActivity(intent)
}
diff --git a/app/src/main/java/org/simple/clinic/widgets/ChipInputAutoCompleteTextView.kt b/app/src/main/java/org/simple/clinic/widgets/ChipInputAutoCompleteTextView.kt
index 823c592ded9..e58e9bef62a 100644
--- a/app/src/main/java/org/simple/clinic/widgets/ChipInputAutoCompleteTextView.kt
+++ b/app/src/main/java/org/simple/clinic/widgets/ChipInputAutoCompleteTextView.kt
@@ -3,7 +3,6 @@ package org.simple.clinic.widgets
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Color
-import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.os.Parcelable
import android.util.AttributeSet
@@ -17,6 +16,7 @@ import android.widget.ArrayAdapter
import androidx.annotation.IdRes
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.res.use
+import androidx.core.graphics.drawable.toDrawable
import androidx.core.os.bundleOf
import androidx.core.view.children
import com.google.android.material.chip.Chip
@@ -233,7 +233,7 @@ class ChipInputAutoCompleteTextView(
private fun dropDownConfig() {
autoCompleteTextView.onItemClickListener = this
autoCompleteTextView.dropDownWidth = MATCH_PARENT
- autoCompleteTextView.setDropDownBackgroundDrawable(ColorDrawable(Color.WHITE))
+ autoCompleteTextView.setDropDownBackgroundDrawable(Color.WHITE.toDrawable())
}
override fun onSaveInstanceState(): Parcelable {
diff --git a/app/src/main/java/org/simple/clinic/widgets/DividerItemDecorator.kt b/app/src/main/java/org/simple/clinic/widgets/DividerItemDecorator.kt
index d244b59d992..e3bd48500ff 100644
--- a/app/src/main/java/org/simple/clinic/widgets/DividerItemDecorator.kt
+++ b/app/src/main/java/org/simple/clinic/widgets/DividerItemDecorator.kt
@@ -6,6 +6,7 @@ import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.view.View
import androidx.core.content.res.use
+import androidx.core.graphics.withSave
import androidx.recyclerview.widget.RecyclerView
import timber.log.Timber
import kotlin.math.roundToInt
@@ -39,20 +40,20 @@ class DividerItemDecorator(
}
private fun drawDividers(canvas: Canvas, parent: RecyclerView) {
- canvas.save()
- val left = marginStart
- val right = parent.width - marginEnd
+ canvas.withSave {
+ val left = marginStart
+ val right = parent.width - marginEnd
- val childCount = parent.childCount
- for (i in 0 until childCount - 1) {
- val child = parent.getChildAt(i)
- parent.getDecoratedBoundsWithMargins(child, mBounds)
- val bottom: Int = mBounds.bottom + child.translationY.roundToInt()
- val top: Int = bottom - divider!!.intrinsicHeight
- divider!!.setBounds(left, top, right, bottom)
- divider!!.draw(canvas)
+ val childCount = parent.childCount
+ for (i in 0 until childCount - 1) {
+ val child = parent.getChildAt(i)
+ parent.getDecoratedBoundsWithMargins(child, mBounds)
+ val bottom: Int = mBounds.bottom + child.translationY.roundToInt()
+ val top: Int = bottom - divider!!.intrinsicHeight
+ divider!!.setBounds(left, top, right, bottom)
+ divider!!.draw(this)
+ }
}
- canvas.restore()
}
override fun getItemOffsets(
diff --git a/app/src/main/java/org/simple/clinic/widgets/PrimarySolidButtonWithFrame.kt b/app/src/main/java/org/simple/clinic/widgets/PrimarySolidButtonWithFrame.kt
index 65c10d38784..ed38b1a5f64 100644
--- a/app/src/main/java/org/simple/clinic/widgets/PrimarySolidButtonWithFrame.kt
+++ b/app/src/main/java/org/simple/clinic/widgets/PrimarySolidButtonWithFrame.kt
@@ -5,6 +5,7 @@ import android.util.AttributeSet
import android.widget.Button
import android.widget.FrameLayout
import androidx.annotation.ColorInt
+import androidx.core.content.withStyledAttributes
import org.simple.clinic.R
class PrimarySolidButtonWithFrame(
@@ -27,14 +28,14 @@ class PrimarySolidButtonWithFrame(
private var buttonBackgroundDisabledResId: Int = 0
init {
- val attrs = context.obtainStyledAttributes(attributeSet, R.styleable.PrimarySolidButtonWithFrame)
+ context.withStyledAttributes(attributeSet, R.styleable.PrimarySolidButtonWithFrame) {
- frameBackgroundEnabledResId = attrs.getColor(R.styleable.PrimarySolidButtonWithFrame_frameBackgroundEnabled, frameBackgroundEnabledResId)
- frameBackgroundDisabledResId = attrs.getColor(R.styleable.PrimarySolidButtonWithFrame_frameBackgroundDisabled, frameBackgroundDisabledResId)
- buttonBackgroundEnabledResId = attrs.getColor(R.styleable.PrimarySolidButtonWithFrame_buttonBackgroundEnabled, buttonBackgroundEnabledResId)
- buttonBackgroundDisabledResId = attrs.getColor(R.styleable.PrimarySolidButtonWithFrame_buttonBackgroundDisabled, buttonBackgroundDisabledResId)
+ frameBackgroundEnabledResId = getColor(R.styleable.PrimarySolidButtonWithFrame_frameBackgroundEnabled, frameBackgroundEnabledResId)
+ frameBackgroundDisabledResId = getColor(R.styleable.PrimarySolidButtonWithFrame_frameBackgroundDisabled, frameBackgroundDisabledResId)
+ buttonBackgroundEnabledResId = getColor(R.styleable.PrimarySolidButtonWithFrame_buttonBackgroundEnabled, buttonBackgroundEnabledResId)
+ buttonBackgroundDisabledResId = getColor(R.styleable.PrimarySolidButtonWithFrame_buttonBackgroundDisabled, buttonBackgroundDisabledResId)
- attrs.recycle()
+ }
verifyColorsSet()
}
diff --git a/app/src/main/java/org/simple/clinic/widgets/ProgressMaterialButton.kt b/app/src/main/java/org/simple/clinic/widgets/ProgressMaterialButton.kt
index 91f90f6c0d8..02a43accbe7 100644
--- a/app/src/main/java/org/simple/clinic/widgets/ProgressMaterialButton.kt
+++ b/app/src/main/java/org/simple/clinic/widgets/ProgressMaterialButton.kt
@@ -3,6 +3,7 @@ package org.simple.clinic.widgets
import android.content.Context
import android.graphics.drawable.Drawable
import android.util.AttributeSet
+import androidx.core.content.withStyledAttributes
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
import com.google.android.material.button.MaterialButton
import org.simple.clinic.R
@@ -16,7 +17,7 @@ class ProgressMaterialButton(
InProgress, Enabled, Disabled
}
- private var buttonState: ButtonState
+ private lateinit var buttonState: ButtonState
private var buttonText: String? = null
private var buttonIcon: Drawable? = null
private var buttonIconGravity: Int
@@ -28,12 +29,12 @@ class ProgressMaterialButton(
buttonIcon = icon
buttonIconGravity = iconGravity
- val typeArray = context.obtainStyledAttributes(attrs, R.styleable.ProgressMaterialButton)
- buttonState = ButtonState.values()[typeArray.getInt(R.styleable.ProgressMaterialButton_buttonState, 0)]
+ context.withStyledAttributes(attrs, R.styleable.ProgressMaterialButton) {
+ buttonState = ButtonState.entries.toTypedArray()[getInt(R.styleable.ProgressMaterialButton_buttonState, 0)]
- setButtonState(buttonState)
+ setButtonState(buttonState)
- typeArray.recycle()
+ }
}
fun setButtonState(buttonState: ButtonState) {
@@ -47,6 +48,7 @@ class ProgressMaterialButton(
progressDrawable.start()
}
+
ButtonState.Enabled -> {
isEnabled = true
isClickable = true
@@ -56,6 +58,7 @@ class ProgressMaterialButton(
progressDrawable.stop()
}
+
ButtonState.Disabled -> {
isEnabled = false
icon = buttonIcon
diff --git a/app/src/main/java/org/simple/clinic/widgets/ToolbarWithTintedNavIcon.kt b/app/src/main/java/org/simple/clinic/widgets/ToolbarWithTintedNavIcon.kt
index 79f8de9926d..5f1b26b682f 100644
--- a/app/src/main/java/org/simple/clinic/widgets/ToolbarWithTintedNavIcon.kt
+++ b/app/src/main/java/org/simple/clinic/widgets/ToolbarWithTintedNavIcon.kt
@@ -1,26 +1,28 @@
package org.simple.clinic.widgets
+import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import androidx.annotation.ColorInt
import androidx.appcompat.widget.Toolbar
+import androidx.core.content.withStyledAttributes
import androidx.core.graphics.drawable.DrawableCompat
import org.simple.clinic.R
-class ToolbarWithTintedNavIcon(context: Context, attrs: AttributeSet) : Toolbar(context, attrs) {
+@SuppressLint("CustomViewStyleable") class ToolbarWithTintedNavIcon(context: Context, attrs: AttributeSet) : Toolbar(context, attrs) {
@ColorInt
- private val navigationIconTint: Int
+ private var navigationIconTint: Int = 0
init {
// Bright red so that nobody forgets to set the value.
val defaultTint = Color.RED
- val attributes = context.obtainStyledAttributes(attrs, R.styleable.ToolbarWithTintedNavIcon)
- navigationIconTint = attributes.getColor(R.styleable.ToolbarWithTintedNavIcon_navigationIconTint, defaultTint)
- attributes.recycle()
+ context.withStyledAttributes(attrs, R.styleable.ToolbarWithTintedNavIcon) {
+ navigationIconTint = getColor(R.styleable.ToolbarWithTintedNavIcon_navigationIconTint, defaultTint)
+ }
val icon = navigationIcon
if (icon != null) {
diff --git a/app/src/main/java/org/simple/clinic/widgets/ViewFlipperWithLayoutEditorPreview.kt b/app/src/main/java/org/simple/clinic/widgets/ViewFlipperWithLayoutEditorPreview.kt
index 4998dafbd70..a87e1db4a58 100644
--- a/app/src/main/java/org/simple/clinic/widgets/ViewFlipperWithLayoutEditorPreview.kt
+++ b/app/src/main/java/org/simple/clinic/widgets/ViewFlipperWithLayoutEditorPreview.kt
@@ -3,6 +3,7 @@ package org.simple.clinic.widgets
import android.content.Context
import android.util.AttributeSet
import android.widget.ViewFlipper
+import androidx.core.content.withStyledAttributes
import io.reactivex.Observable
import io.reactivex.subjects.BehaviorSubject
import io.reactivex.subjects.Subject
@@ -21,9 +22,9 @@ class ViewFlipperWithLayoutEditorPreview(
init {
if (isInEditMode) {
- val attributes = context.obtainStyledAttributes(attrs, R.styleable.ViewFlipperWithLayoutEditorPreview)
- childToDisplayPostInflate = attributes.getInt(R.styleable.ViewFlipperWithLayoutEditorPreview_debug_displayedChild, 0)
- attributes.recycle()
+ context.withStyledAttributes(attrs, R.styleable.ViewFlipperWithLayoutEditorPreview) {
+ childToDisplayPostInflate = getInt(R.styleable.ViewFlipperWithLayoutEditorPreview_debug_displayedChild, 0)
+ }
}
displayedChildChangesSubject.onNext(displayedChild)
}
diff --git a/app/src/main/java/org/simple/clinic/widgets/Views.kt b/app/src/main/java/org/simple/clinic/widgets/Views.kt
index 4c78fb7033b..53bf6ce14ac 100644
--- a/app/src/main/java/org/simple/clinic/widgets/Views.kt
+++ b/app/src/main/java/org/simple/clinic/widgets/Views.kt
@@ -4,7 +4,6 @@ import android.content.Context
import android.content.res.Resources
import android.graphics.Rect
import android.graphics.drawable.Drawable
-import android.os.Build
import android.text.TextWatcher
import android.util.DisplayMetrics
import android.view.MenuItem
@@ -27,6 +26,7 @@ import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.drawable.DrawableCompat
import androidx.core.view.doOnDetach
+import androidx.core.view.isGone
import androidx.core.widget.NestedScrollView
import androidx.dynamicanimation.animation.DynamicAnimation
import androidx.dynamicanimation.animation.DynamicAnimation.ALPHA
@@ -52,7 +52,6 @@ import io.reactivex.Observable
import org.simple.clinic.R
import timber.log.Timber
import java.time.Duration
-import androidx.core.view.isGone
fun EditText.showKeyboard() {
val openKeyboard = Runnable {
@@ -284,12 +283,7 @@ fun dpToPx(dp: Float): Float {
fun dpToPx(dp: Int) = dpToPx(dp.toFloat())
fun TextView.setTextAppearanceCompat(@StyleRes resourceId: Int) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- setTextAppearance(resourceId)
- } else {
- @Suppress("DEPRECATION")
- setTextAppearance(context, resourceId)
- }
+ setTextAppearance(resourceId)
}
fun ScrollView.scrollToChild(view: View, onScrollComplete: () -> Unit = {}) {
@@ -306,7 +300,7 @@ fun ScrollView.scrollToChild(view: View, onScrollComplete: () -> Unit = {}) {
}
fun NestedScrollView.scrollToChild(view: View, onScrollComplete: () -> Unit = {}) {
- val runnable= Runnable { onScrollComplete() }
+ val runnable = Runnable { onScrollComplete() }
post {
val distanceToScrollFromTop = view.topRelativeTo(this)
diff --git a/app/src/main/res/color-v23/appointment_date_stepper.xml b/app/src/main/res/color-v23/appointment_date_stepper.xml
deleted file mode 100644
index 29911380149..00000000000
--- a/app/src/main/res/color-v23/appointment_date_stepper.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/color-v23/changelanguage_radio_button.xml b/app/src/main/res/color-v23/changelanguage_radio_button.xml
deleted file mode 100644
index 69a4ea08b11..00000000000
--- a/app/src/main/res/color-v23/changelanguage_radio_button.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/color-v23/color_on_toolbar_primary_72.xml b/app/src/main/res/color-v23/color_on_toolbar_primary_72.xml
deleted file mode 100644
index 773892d9806..00000000000
--- a/app/src/main/res/color-v23/color_on_toolbar_primary_72.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
diff --git a/app/src/main/res/color-v23/deletereason_radio_button.xml b/app/src/main/res/color-v23/deletereason_radio_button.xml
deleted file mode 100644
index aaae5529005..00000000000
--- a/app/src/main/res/color-v23/deletereason_radio_button.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/color-v23/editpatient_radio_button.xml b/app/src/main/res/color-v23/editpatient_radio_button.xml
deleted file mode 100644
index aaae5529005..00000000000
--- a/app/src/main/res/color-v23/editpatient_radio_button.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/color-v23/intro_video_subtitle.xml b/app/src/main/res/color-v23/intro_video_subtitle.xml
deleted file mode 100644
index edf4740f774..00000000000
--- a/app/src/main/res/color-v23/intro_video_subtitle.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
diff --git a/app/src/main/res/color-v23/patiententry_radio_button.xml b/app/src/main/res/color-v23/patiententry_radio_button.xml
deleted file mode 100644
index aaae5529005..00000000000
--- a/app/src/main/res/color-v23/patiententry_radio_button.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/color-v23/removeappointment_radio_button.xml b/app/src/main/res/color-v23/removeappointment_radio_button.xml
deleted file mode 100644
index aaae5529005..00000000000
--- a/app/src/main/res/color-v23/removeappointment_radio_button.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/color-v23/snackbar_subtitle.xml b/app/src/main/res/color-v23/snackbar_subtitle.xml
deleted file mode 100644
index b3274e9b166..00000000000
--- a/app/src/main/res/color-v23/snackbar_subtitle.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
diff --git a/app/src/main/res/color/appointment_date_stepper.xml b/app/src/main/res/color/appointment_date_stepper.xml
index d0bd304fe02..29911380149 100644
--- a/app/src/main/res/color/appointment_date_stepper.xml
+++ b/app/src/main/res/color/appointment_date_stepper.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/app/src/main/res/color/changelanguage_radio_button.xml b/app/src/main/res/color/changelanguage_radio_button.xml
index 2dc971cd754..69a4ea08b11 100644
--- a/app/src/main/res/color/changelanguage_radio_button.xml
+++ b/app/src/main/res/color/changelanguage_radio_button.xml
@@ -1,5 +1,5 @@
-
+
-
+
diff --git a/app/src/main/res/color/color_on_toolbar_primary_72.xml b/app/src/main/res/color/color_on_toolbar_primary_72.xml
index 4a2bded758d..773892d9806 100644
--- a/app/src/main/res/color/color_on_toolbar_primary_72.xml
+++ b/app/src/main/res/color/color_on_toolbar_primary_72.xml
@@ -1,4 +1,4 @@
-
+
-
+
diff --git a/app/src/main/res/color/deletereason_radio_button.xml b/app/src/main/res/color/deletereason_radio_button.xml
index a7163870e14..aaae5529005 100644
--- a/app/src/main/res/color/deletereason_radio_button.xml
+++ b/app/src/main/res/color/deletereason_radio_button.xml
@@ -1,5 +1,5 @@
-
+
-
+
diff --git a/app/src/main/res/color/editpatient_radio_button.xml b/app/src/main/res/color/editpatient_radio_button.xml
index a7163870e14..aaae5529005 100644
--- a/app/src/main/res/color/editpatient_radio_button.xml
+++ b/app/src/main/res/color/editpatient_radio_button.xml
@@ -1,5 +1,5 @@
-
+
-
+
diff --git a/app/src/main/res/color/intro_video_subtitle.xml b/app/src/main/res/color/intro_video_subtitle.xml
index c0802300e62..edf4740f774 100644
--- a/app/src/main/res/color/intro_video_subtitle.xml
+++ b/app/src/main/res/color/intro_video_subtitle.xml
@@ -1,4 +1,4 @@
-
+
diff --git a/app/src/main/res/color/patiententry_radio_button.xml b/app/src/main/res/color/patiententry_radio_button.xml
index a7163870e14..aaae5529005 100644
--- a/app/src/main/res/color/patiententry_radio_button.xml
+++ b/app/src/main/res/color/patiententry_radio_button.xml
@@ -1,5 +1,5 @@
-
+
-
+
diff --git a/app/src/main/res/color/removeappointment_radio_button.xml b/app/src/main/res/color/removeappointment_radio_button.xml
index a7163870e14..aaae5529005 100644
--- a/app/src/main/res/color/removeappointment_radio_button.xml
+++ b/app/src/main/res/color/removeappointment_radio_button.xml
@@ -1,5 +1,5 @@
-
+
-
+
diff --git a/app/src/main/res/color/snackbar_subtitle.xml b/app/src/main/res/color/snackbar_subtitle.xml
index 844f0d14579..b3274e9b166 100644
--- a/app/src/main/res/color/snackbar_subtitle.xml
+++ b/app/src/main/res/color/snackbar_subtitle.xml
@@ -1,4 +1,4 @@
-
+
diff --git a/app/src/main/res/drawable-v26/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml
similarity index 100%
rename from app/src/main/res/drawable-v26/ic_launcher_foreground.xml
rename to app/src/main/res/drawable/ic_launcher_foreground.xml
diff --git a/app/src/main/res/layout/patient_edit_alternate_id_view.xml b/app/src/main/res/layout/patient_edit_alternate_id_view.xml
index 26508e49c1e..993ba6c0b76 100644
--- a/app/src/main/res/layout/patient_edit_alternate_id_view.xml
+++ b/app/src/main/res/layout/patient_edit_alternate_id_view.xml
@@ -13,5 +13,4 @@
android:textColor="?attr/colorOnSurface"
app:drawableStartCompat="@drawable/patient_id_card"
app:drawableTint="@color/color_on_surface_67"
- tools:targetApi="m"
tools:text="12345678" />
diff --git a/app/src/main/res/layout/patient_edit_bp_passport_view.xml b/app/src/main/res/layout/patient_edit_bp_passport_view.xml
index 671ae03b4cd..60dd9b0c8be 100644
--- a/app/src/main/res/layout/patient_edit_bp_passport_view.xml
+++ b/app/src/main/res/layout/patient_edit_bp_passport_view.xml
@@ -13,5 +13,4 @@
android:textColor="?attr/colorOnSurface"
app:drawableStartCompat="@drawable/patient_id_card"
app:drawableTint="@color/color_on_surface_67"
- tools:targetApi="m"
tools:text="12345678" />
diff --git a/app/src/main/res/layout/patient_entry_alternate_id_view.xml b/app/src/main/res/layout/patient_entry_alternate_id_view.xml
index fa1f241d16d..849955e792a 100644
--- a/app/src/main/res/layout/patient_entry_alternate_id_view.xml
+++ b/app/src/main/res/layout/patient_entry_alternate_id_view.xml
@@ -13,5 +13,4 @@
android:textColor="?attr/colorOnSurface"
app:drawableStartCompat="@drawable/patient_id_card"
app:drawableTint="@color/color_on_surface_67"
- tools:targetApi="m"
tools:text="1234123412341234" />
diff --git a/app/src/main/res/layout/screen_new_medical_history.xml b/app/src/main/res/layout/screen_new_medical_history.xml
index 8da937e03a6..61ecef7a30b 100644
--- a/app/src/main/res/layout/screen_new_medical_history.xml
+++ b/app/src/main/res/layout/screen_new_medical_history.xml
@@ -99,24 +99,10 @@
-
-
-
-
-
+ android:layout_height="match_parent" />
diff --git a/app/src/main/res/mipmap-anydpi/ic_launcher.xml b/app/src/main/res/mipmap-anydpi/ic_launcher.xml
new file mode 100644
index 00000000000..370b2a4527c
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi/ic_launcher.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index 4099cb6b081..00000000000
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index bb46aa7bab6..00000000000
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index e04873bf664..00000000000
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 3ac4c94c771..00000000000
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index 119bee1c1a1..00000000000
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/main/res/values-am-rET/strings.xml b/app/src/main/res/values-am-rET/strings.xml
index 0cd49103209..1e5bd79c0c3 100644
--- a/app/src/main/res/values-am-rET/strings.xml
+++ b/app/src/main/res/values-am-rET/strings.xml
@@ -488,6 +488,7 @@
ታሪክ
ምርመራ (አስፈላጊ)
+ ትምባሆ አጠቃቀም
ምንም መድሃኒቶች አልታከሉም
diff --git a/app/src/main/res/values-bn-rBD/strings.xml b/app/src/main/res/values-bn-rBD/strings.xml
index 2bb11b9864b..5a8e02231c5 100644
--- a/app/src/main/res/values-bn-rBD/strings.xml
+++ b/app/src/main/res/values-bn-rBD/strings.xml
@@ -338,7 +338,6 @@
ইতিহাস
গত মাসে রোগী কি কোনও সময় বিপি-র ওষুধ খেয়েছিলেন? (আবশ্যিক)
গত মাসে রোগী কি কোনও সময় ডায়াবেটিসের ওষুধ খেয়েছিলেন? (আবশ্যিক)
- তিনি কি ধূমপান করেন ?
কোনটিই নয়
@@ -490,6 +489,7 @@
ইতিহাস
রোগ নির্ণয় (প্রয়োজন)
+ তামাক সেবন
কোনো ওষুধ যোগ করা হয়নি
diff --git a/app/src/main/res/values-om-rET/strings.xml b/app/src/main/res/values-om-rET/strings.xml
index c10128a83a4..02e8fdd452b 100644
--- a/app/src/main/res/values-om-rET/strings.xml
+++ b/app/src/main/res/values-om-rET/strings.xml
@@ -489,6 +489,7 @@
Seenaa
Qorannoo (Barbaadameera)
+ Fayyadama tamboo
Qorichi kamiyyuu hin dabalamne
diff --git a/app/src/main/res/values-si-rLK/strings.xml b/app/src/main/res/values-si-rLK/strings.xml
index ffab4299f85..4d15283f5c9 100644
--- a/app/src/main/res/values-si-rLK/strings.xml
+++ b/app/src/main/res/values-si-rLK/strings.xml
@@ -338,7 +338,6 @@
ඉතිහාසය
රෝගියා පසුගිය මාසය තුල කිසියම් දිනකදී රුධිර පීඩනය පාලනය කිරීම සඳහා ඖෂධයක් ලබා ගත්තේද? (අවශ්යයි)
රෝගියා පසුගිය මාසය තුල කිසියම් දිනකදී දියවැඩියාව පාලනය කිරීම සඳහා ඖෂධයක් ලබා ගත්තේද? (අවශ්යයි)
- දුම් බොනවාද?
කිසිවක් නැත
@@ -490,6 +489,7 @@
ඉතිහාසය
රෝග නිශ්චය (අවශ්යයි)
+ දුම්කොළ ආශ්රිත නිෂ්පාදන භාවිතය
කිසිදු ඖෂධයක් එකතු කර නැත
diff --git a/app/src/main/res/values-ta-rLK/strings.xml b/app/src/main/res/values-ta-rLK/strings.xml
index d1191678c47..1e881fe5026 100644
--- a/app/src/main/res/values-ta-rLK/strings.xml
+++ b/app/src/main/res/values-ta-rLK/strings.xml
@@ -338,7 +338,6 @@
வரலாறு
கடந்த மாதத்தில் நோயாளர் எப்போதாவது பிபி குளிகைகளை எடுத்துக்கொண்டாரா? (தேவையானவை)
கடந்த மாதத்தில் நோயாளர் எப்போதாவது நீரழிவுநோய் மருந்துகளை எடுத்துக்கொண்டாரா? (தேவையானவை)
- புகைபிடிக்கிறதா?
எதுவுமில்லை
@@ -490,6 +489,7 @@
வரலாறு
நோய் கண்டறிதல் (தேவை)
+ புகையிலை பாவனை
குளிகைகள் எதுவும் சேர்க்கப்படவில்லை
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index e2e94cec46b..fd5f7872696 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -397,7 +397,8 @@
Did patient take BP medicines any time in the last month? (Required)
Is patient starting Simple treatment protocol for Hypertension? (Required)
Did patient take diabetes medicines any time in the last month? (Required)
- Smokes?
+ Smokes
+ Smokeless tobacco
None
@@ -573,6 +574,7 @@
History
Diagnosis (Required)
+ Tobacco use
No medicines added
diff --git a/app/src/sandbox/res/drawable-v26/ic_launcher_foreground.xml b/app/src/sandbox/res/drawable/ic_launcher_foreground.xml
similarity index 100%
rename from app/src/sandbox/res/drawable-v26/ic_launcher_foreground.xml
rename to app/src/sandbox/res/drawable/ic_launcher_foreground.xml
diff --git a/app/src/sandbox/res/mipmap-anydpi/ic_launcher.xml b/app/src/sandbox/res/mipmap-anydpi/ic_launcher.xml
new file mode 100644
index 00000000000..370b2a4527c
--- /dev/null
+++ b/app/src/sandbox/res/mipmap-anydpi/ic_launcher.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/app/src/sandbox/res/mipmap-hdpi/ic_launcher.png b/app/src/sandbox/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index c850717b4fd..00000000000
Binary files a/app/src/sandbox/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/sandbox/res/mipmap-mdpi/ic_launcher.png b/app/src/sandbox/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index 4a0d5ac6c1d..00000000000
Binary files a/app/src/sandbox/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/sandbox/res/mipmap-xhdpi/ic_launcher.png b/app/src/sandbox/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index ed6230e1677..00000000000
Binary files a/app/src/sandbox/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/sandbox/res/mipmap-xxhdpi/ic_launcher.png b/app/src/sandbox/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 392ecb5dc95..00000000000
Binary files a/app/src/sandbox/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/sandbox/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/sandbox/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index edfa17eb0f1..00000000000
Binary files a/app/src/sandbox/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/security/res/drawable-v26/ic_launcher_foreground.xml b/app/src/security/res/drawable/ic_launcher_foreground.xml
similarity index 100%
rename from app/src/security/res/drawable-v26/ic_launcher_foreground.xml
rename to app/src/security/res/drawable/ic_launcher_foreground.xml
diff --git a/app/src/security/res/mipmap-anydpi/ic_launcher.xml b/app/src/security/res/mipmap-anydpi/ic_launcher.xml
new file mode 100644
index 00000000000..370b2a4527c
--- /dev/null
+++ b/app/src/security/res/mipmap-anydpi/ic_launcher.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/app/src/security/res/mipmap-hdpi/ic_launcher.png b/app/src/security/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index c850717b4fd..00000000000
Binary files a/app/src/security/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/security/res/mipmap-mdpi/ic_launcher.png b/app/src/security/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index 4a0d5ac6c1d..00000000000
Binary files a/app/src/security/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/security/res/mipmap-xhdpi/ic_launcher.png b/app/src/security/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index ed6230e1677..00000000000
Binary files a/app/src/security/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/security/res/mipmap-xxhdpi/ic_launcher.png b/app/src/security/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 392ecb5dc95..00000000000
Binary files a/app/src/security/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/security/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/security/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index edfa17eb0f1..00000000000
Binary files a/app/src/security/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/staging/res/drawable-v26/ic_launcher_foreground.xml b/app/src/staging/res/drawable/ic_launcher_foreground.xml
similarity index 100%
rename from app/src/staging/res/drawable-v26/ic_launcher_foreground.xml
rename to app/src/staging/res/drawable/ic_launcher_foreground.xml
diff --git a/app/src/staging/res/mipmap-anydpi/ic_launcher.xml b/app/src/staging/res/mipmap-anydpi/ic_launcher.xml
new file mode 100644
index 00000000000..370b2a4527c
--- /dev/null
+++ b/app/src/staging/res/mipmap-anydpi/ic_launcher.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/app/src/staging/res/mipmap-hdpi/ic_launcher.png b/app/src/staging/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index 5634fe17066..00000000000
Binary files a/app/src/staging/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/staging/res/mipmap-mdpi/ic_launcher.png b/app/src/staging/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index 12a9f092357..00000000000
Binary files a/app/src/staging/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/staging/res/mipmap-xhdpi/ic_launcher.png b/app/src/staging/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index e8064296942..00000000000
Binary files a/app/src/staging/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/staging/res/mipmap-xxhdpi/ic_launcher.png b/app/src/staging/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 70f2aa76597..00000000000
Binary files a/app/src/staging/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/staging/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/staging/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index 2db13770c0a..00000000000
Binary files a/app/src/staging/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/test/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryInitTest.kt b/app/src/test/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryInitTest.kt
index 9f1904426b1..3491c94f103 100644
--- a/app/src/test/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryInitTest.kt
+++ b/app/src/test/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryInitTest.kt
@@ -11,7 +11,11 @@ import org.simple.clinic.appconfig.Country
class NewMedicalHistoryInitTest {
private val country = TestData.country(isoCountryCode = Country.INDIA)
- private val defaultModel = NewMedicalHistoryModel.default(country, false)
+ private val defaultModel = NewMedicalHistoryModel.default(
+ country = country,
+ showIsSmokingQuestion = false,
+ showSmokelessTobaccoQuestion = false
+ )
private val initSpec = InitSpec(NewMedicalHistoryInit())
diff --git a/app/src/test/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryScreenLogicTest.kt b/app/src/test/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryScreenLogicTest.kt
index 100fe5065e7..18aab4b1ccd 100644
--- a/app/src/test/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryScreenLogicTest.kt
+++ b/app/src/test/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryScreenLogicTest.kt
@@ -13,6 +13,7 @@ import org.mockito.kotlin.mock
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
+import org.simple.clinic.TestData
import org.simple.clinic.appconfig.Country
import org.simple.clinic.medicalhistory.Answer.No
import org.simple.clinic.medicalhistory.Answer.Unanswered
@@ -25,6 +26,7 @@ import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.HasHadAStroke
import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.IsOnDiabetesTreatment
import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.IsOnHypertensionTreatment
import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.IsSmoking
+import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.IsUsingSmokelessTobacco
import org.simple.clinic.medicalhistory.MedicalHistoryRepository
import org.simple.clinic.medicalhistory.OngoingMedicalHistoryEntry
import org.simple.clinic.patient.Gender
@@ -32,13 +34,12 @@ import org.simple.clinic.patient.OngoingNewPatientEntry
import org.simple.clinic.patient.OngoingNewPatientEntry.PersonalDetails
import org.simple.clinic.patient.PatientProfile
import org.simple.clinic.patient.PatientRepository
+import org.simple.clinic.util.RxErrorsRule
import org.simple.clinic.util.scheduler.TrampolineSchedulersProvider
+import org.simple.clinic.uuid.FakeUuidGenerator
import org.simple.clinic.uuid.UuidGenerator
import org.simple.clinic.widgets.UiEvent
import org.simple.mobius.migration.MobiusTestFixture
-import org.simple.clinic.TestData
-import org.simple.clinic.util.RxErrorsRule
-import org.simple.clinic.uuid.FakeUuidGenerator
import java.time.format.DateTimeFormatter
import java.util.Locale
import java.util.UUID
@@ -122,6 +123,7 @@ class NewMedicalHistoryScreenLogicTest {
uiEvents.onNext(NewMedicalHistoryAnswerToggled(DiagnosedWithDiabetes, Yes))
uiEvents.onNext(NewMedicalHistoryAnswerToggled(IsOnDiabetesTreatment, Yes))
uiEvents.onNext(NewMedicalHistoryAnswerToggled(IsSmoking, Yes))
+ uiEvents.onNext(NewMedicalHistoryAnswerToggled(IsUsingSmokelessTobacco, Yes))
uiEvents.onNext(SaveMedicalHistoryClicked())
// then
@@ -148,6 +150,7 @@ class NewMedicalHistoryScreenLogicTest {
hasDiabetes = Yes,
isOnDiabetesTreatment = Yes,
isSmoking = Yes,
+ isUsingSmokelessTobacco = Yes,
)
)
verify(uiActions).openPatientSummaryScreen(savedPatient.patientUuid)
@@ -213,6 +216,7 @@ class NewMedicalHistoryScreenLogicTest {
hasDiabetes = Unanswered,
isOnHypertensionTreatment = Yes,
isSmoking = Unanswered,
+ isUsingSmokelessTobacco = Unanswered,
))
verify(uiActions).openPatientSummaryScreen(savedPatient.patientUuid)
}
@@ -253,6 +257,7 @@ class NewMedicalHistoryScreenLogicTest {
uiEvents.onNext(NewMedicalHistoryAnswerToggled(HasHadAKidneyDisease, Yes))
uiEvents.onNext(NewMedicalHistoryAnswerToggled(DiagnosedWithDiabetes, Yes))
uiEvents.onNext(NewMedicalHistoryAnswerToggled(IsSmoking, Yes))
+ uiEvents.onNext(NewMedicalHistoryAnswerToggled(IsUsingSmokelessTobacco, Yes))
// Updated answers
uiEvents.onNext(NewMedicalHistoryAnswerToggled(DiagnosedWithHypertension, Yes))
@@ -262,6 +267,7 @@ class NewMedicalHistoryScreenLogicTest {
uiEvents.onNext(NewMedicalHistoryAnswerToggled(DiagnosedWithDiabetes, No))
uiEvents.onNext(NewMedicalHistoryAnswerToggled(IsOnHypertensionTreatment(Country.INDIA), Yes))
uiEvents.onNext(NewMedicalHistoryAnswerToggled(IsSmoking, No))
+ uiEvents.onNext(NewMedicalHistoryAnswerToggled(IsUsingSmokelessTobacco, No))
uiEvents.onNext(SaveMedicalHistoryClicked())
@@ -289,6 +295,7 @@ class NewMedicalHistoryScreenLogicTest {
hasDiabetes = No,
isOnHypertensionTreatment = Yes,
isSmoking = No,
+ isUsingSmokelessTobacco = No,
)
)
verify(uiActions).openPatientSummaryScreen(savedPatient.patientUuid)
@@ -320,7 +327,11 @@ class NewMedicalHistoryScreenLogicTest {
testFixture = MobiusTestFixture(
events = uiEvents.ofType(),
- defaultModel = NewMedicalHistoryModel.default(country, true),
+ defaultModel = NewMedicalHistoryModel.default(
+ country = country,
+ showIsSmokingQuestion = true,
+ showSmokelessTobaccoQuestion = true
+ ),
init = NewMedicalHistoryInit(),
update = NewMedicalHistoryUpdate(),
effectHandler = effectHandler,
diff --git a/app/src/test/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryUiRendererTest.kt b/app/src/test/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryUiRendererTest.kt
index afc05720e1f..84fb19a6398 100644
--- a/app/src/test/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryUiRendererTest.kt
+++ b/app/src/test/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryUiRendererTest.kt
@@ -4,6 +4,7 @@ import org.junit.Test
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyNoMoreInteractions
+import org.simple.clinic.TestData
import org.simple.clinic.appconfig.Country
import org.simple.clinic.facility.FacilityConfig
import org.simple.clinic.medicalhistory.Answer.No
@@ -15,7 +16,7 @@ import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.HasHadAHeartAttac
import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.HasHadAKidneyDisease
import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.HasHadAStroke
import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.IsSmoking
-import org.simple.clinic.TestData
+import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.IsUsingSmokelessTobacco
import java.util.UUID
class NewMedicalHistoryUiRendererTest {
@@ -43,7 +44,11 @@ class NewMedicalHistoryUiRendererTest {
)
private val country = TestData.country(isoCountryCode = Country.INDIA)
- private val defaultModel = NewMedicalHistoryModel.default(country, false)
+ private val defaultModel = NewMedicalHistoryModel.default(
+ country = country,
+ showIsSmokingQuestion = false,
+ showSmokelessTobaccoQuestion = false
+ )
private val ui = mock()
private val uiRenderer = NewMedicalHistoryUiRenderer(ui)
@@ -71,6 +76,7 @@ class NewMedicalHistoryUiRendererTest {
verify(ui).showDiabetesHistorySection()
verify(ui).renderAnswerForQuestion(DiagnosedWithDiabetes, Unanswered)
verify(ui).hideCurrentSmokerQuestion()
+ verify(ui).hideSmokelessTobaccoQuestion()
verifyNoMoreInteractions(ui)
}
@@ -164,7 +170,11 @@ class NewMedicalHistoryUiRendererTest {
fun `when patient has hypertension and country is not from india, then don't show hypertension treatment question`() {
// given
val bangladesh = TestData.country(isoCountryCode = Country.BANGLADESH)
- val model = NewMedicalHistoryModel.default(country = bangladesh, false)
+ val model = NewMedicalHistoryModel.default(
+ country = bangladesh,
+ showIsSmokingQuestion = false,
+ showSmokelessTobaccoQuestion = false
+ )
.currentFacilityLoaded(facilityWithDiabetesManagementEnabled)
.answerChanged(DiagnosedWithHypertension, Yes)
@@ -230,7 +240,11 @@ class NewMedicalHistoryUiRendererTest {
fun `when diabetes management is enabled and patient has diabetes and is not from india, then don't show diabetes treatment question`() {
// given
val bangladesh = TestData.country(isoCountryCode = Country.BANGLADESH)
- val model = NewMedicalHistoryModel.default(country = bangladesh, false)
+ val model = NewMedicalHistoryModel.default(
+ country = bangladesh,
+ showIsSmokingQuestion = false,
+ showSmokelessTobaccoQuestion = false
+ )
.currentFacilityLoaded(facilityWithDiabetesManagementEnabled)
.answerChanged(DiagnosedWithDiabetes, Yes)
@@ -274,12 +288,17 @@ class NewMedicalHistoryUiRendererTest {
@Test
fun `when show smoker question is enabled, then show current smoker question`() {
// given
- val model = NewMedicalHistoryModel.default(country, true)
+ val model = NewMedicalHistoryModel.default(
+ country = country,
+ showIsSmokingQuestion = true,
+ showSmokelessTobaccoQuestion = false
+ )
.answerChanged(DiagnosedWithHypertension, Unanswered)
.answerChanged(HasHadAHeartAttack, Yes)
.answerChanged(HasHadAStroke, No)
.answerChanged(HasHadAKidneyDisease, Unanswered)
.answerChanged(IsSmoking, No)
+ .answerChanged(IsUsingSmokelessTobacco, No)
// when
uiRenderer.render(model)
@@ -295,6 +314,7 @@ class NewMedicalHistoryUiRendererTest {
verify(ui).showDiabetesHistorySection()
verify(ui).renderAnswerForQuestion(DiagnosedWithDiabetes, Unanswered)
verify(ui).showCurrentSmokerQuestion()
+ verify(ui).hideSmokelessTobaccoQuestion()
verify(ui).renderAnswerForQuestion(IsSmoking, No)
verifyNoMoreInteractions(ui)
}
@@ -302,7 +322,11 @@ class NewMedicalHistoryUiRendererTest {
@Test
fun `when show smoker question is disabled, then hide current smoker question`() {
// given
- val model = NewMedicalHistoryModel.default(country, false)
+ val model = NewMedicalHistoryModel.default(
+ country = country,
+ showIsSmokingQuestion = false,
+ showSmokelessTobaccoQuestion = false,
+ )
.answerChanged(DiagnosedWithHypertension, Unanswered)
.answerChanged(HasHadAHeartAttack, Yes)
.answerChanged(HasHadAStroke, No)
@@ -322,14 +346,50 @@ class NewMedicalHistoryUiRendererTest {
verify(ui).showDiabetesHistorySection()
verify(ui).renderAnswerForQuestion(DiagnosedWithDiabetes, Unanswered)
verify(ui).hideCurrentSmokerQuestion()
+ verify(ui).hideSmokelessTobaccoQuestion()
verifyNoMoreInteractions(ui)
}
+ @Test
+ fun `when show smokeless tobacco question is enabled, then show smokeless tobacco question`() {
+ // given
+ val model = NewMedicalHistoryModel.default(
+ country = country,
+ showIsSmokingQuestion = true,
+ showSmokelessTobaccoQuestion = true
+ )
+ .answerChanged(DiagnosedWithHypertension, Unanswered)
+ .answerChanged(HasHadAHeartAttack, Yes)
+ .answerChanged(HasHadAStroke, No)
+ .answerChanged(HasHadAKidneyDisease, Unanswered)
+ .answerChanged(IsSmoking, No)
+ .answerChanged(IsUsingSmokelessTobacco, No)
+
+ // when
+ uiRenderer.render(model)
+
+ // then
+ verify(ui).renderDiagnosisAnswer(DiagnosedWithHypertension, Unanswered)
+ verify(ui).hideHypertensionTreatmentQuestion()
+ verify(ui).renderAnswerForQuestion(HasHadAHeartAttack, Yes)
+ verify(ui).renderAnswerForQuestion(HasHadAStroke, No)
+ verify(ui).renderAnswerForQuestion(HasHadAKidneyDisease, Unanswered)
+ verify(ui).hideNextButtonProgress()
+ verify(ui).hideDiabetesDiagnosisView()
+ verify(ui).showDiabetesHistorySection()
+ verify(ui).renderAnswerForQuestion(DiagnosedWithDiabetes, Unanswered)
+ verify(ui).showCurrentSmokerQuestion()
+ verify(ui).showSmokelessTobaccoQuestion()
+ verify(ui).renderAnswerForQuestion(IsSmoking, No)
+ verify(ui).renderAnswerForQuestion(IsUsingSmokelessTobacco, No)
+ verifyNoMoreInteractions(ui)
+ }
private fun verifyImplicitRenders() {
verify(ui).renderAnswerForQuestion(HasHadAHeartAttack, Unanswered)
verify(ui).renderAnswerForQuestion(HasHadAStroke, Unanswered)
verify(ui).renderAnswerForQuestion(HasHadAKidneyDisease, Unanswered)
verify(ui).hideCurrentSmokerQuestion()
+ verify(ui).hideSmokelessTobaccoQuestion()
}
}
diff --git a/app/src/test/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryUpdateTest.kt b/app/src/test/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryUpdateTest.kt
index 595b8a17dee..275434c0f98 100644
--- a/app/src/test/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryUpdateTest.kt
+++ b/app/src/test/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryUpdateTest.kt
@@ -9,6 +9,7 @@ import com.spotify.mobius.test.UpdateSpec.assertThatNext
import junitparams.JUnitParamsRunner
import org.junit.Test
import org.junit.runner.RunWith
+import org.simple.clinic.TestData
import org.simple.clinic.appconfig.Country
import org.simple.clinic.facility.FacilityConfig
import org.simple.clinic.medicalhistory.Answer.No
@@ -19,14 +20,17 @@ import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.DiagnosedWithHype
import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.IsOnDiabetesTreatment
import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.IsOnHypertensionTreatment
import org.simple.clinic.patient.OngoingNewPatientEntry
-import org.simple.clinic.TestData
import java.util.UUID
@RunWith(JUnitParamsRunner::class)
class NewMedicalHistoryUpdateTest {
private val country = TestData.country(isoCountryCode = Country.INDIA)
- private val defaultModel = NewMedicalHistoryModel.default(country, false)
+ private val defaultModel = NewMedicalHistoryModel.default(
+ country = country,
+ showIsSmokingQuestion = false,
+ showSmokelessTobaccoQuestion = false
+ )
private val facilityWithDiabetesManagementEnabled = TestData.facility(
uuid = UUID.fromString("3c7bc1c8-1bb6-4c3a-b6d0-52700bdaac5c"),
facilityConfig = FacilityConfig(
@@ -224,7 +228,11 @@ class NewMedicalHistoryUpdateTest {
@Test
fun `when save is clicked and patient is diagnosed with hypertension and ongoing hypertension treatment question is not answered and selected country is not india, then register patient`() {
val bangladesh = TestData.country(isoCountryCode = Country.BANGLADESH)
- val model = NewMedicalHistoryModel.default(country = bangladesh, false)
+ val model = NewMedicalHistoryModel.default(
+ country = bangladesh,
+ showIsSmokingQuestion = false,
+ showSmokelessTobaccoQuestion = false
+ )
.ongoingPatientEntryLoaded(patientEntry)
.currentFacilityLoaded(facilityWithDiabetesManagementEnabled)
.answerChanged(DiagnosedWithHypertension, Yes)
diff --git a/app/src/test/java/org/simple/clinic/summary/PatientSummaryUpdateTest.kt b/app/src/test/java/org/simple/clinic/summary/PatientSummaryUpdateTest.kt
index 558bcc6d24f..9dc967eec31 100644
--- a/app/src/test/java/org/simple/clinic/summary/PatientSummaryUpdateTest.kt
+++ b/app/src/test/java/org/simple/clinic/summary/PatientSummaryUpdateTest.kt
@@ -2535,11 +2535,12 @@ class PatientSummaryUpdateTest {
fun `when smoking is answered, then update the smoking status`() {
updateSpec
.given(defaultModel)
- .whenEvent(SmokingStatusAnswered(
- isSmoker = No
+ .whenEvent(TobaccoUseAnswered(
+ isSmoker = No,
+ isUsingSmokelessTobacco = Yes
))
.then(assertThatNext(
- hasEffects(UpdateSmokingStatus(patientId = patientUuid, isSmoker = No)),
+ hasEffects(UpdateTobaccoUse(patientId = patientUuid, isSmoker = No, isUsingSmokelessTobacco = Yes)),
hasNoModel()
))
}
diff --git a/app/src/test/java/org/simple/clinic/summary/medicalhistory/MedicalHistorySummaryLogicTest.kt b/app/src/test/java/org/simple/clinic/summary/medicalhistory/MedicalHistorySummaryLogicTest.kt
index 5211551aedb..ccb402f62ce 100644
--- a/app/src/test/java/org/simple/clinic/summary/medicalhistory/MedicalHistorySummaryLogicTest.kt
+++ b/app/src/test/java/org/simple/clinic/summary/medicalhistory/MedicalHistorySummaryLogicTest.kt
@@ -30,6 +30,7 @@ import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.HasHadAStroke
import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.IsOnDiabetesTreatment
import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.IsOnHypertensionTreatment
import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.IsSmoking
+import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.IsUsingSmokelessTobacco
import org.simple.clinic.medicalhistory.MedicalHistoryRepository
import org.simple.clinic.util.TestUtcClock
import org.simple.clinic.util.randomMedicalHistoryAnswer
@@ -99,6 +100,7 @@ class MedicalHistorySummaryLogicTest {
verify(ui).hideDiagnosisView()
verify(ui).populateMedicalHistory(medicalHistory)
verify(ui).hideCurrentSmokerQuestion()
+ verify(ui).hideSmokelessTobaccoQuestion()
verifyNoMoreInteractions(ui)
}
@@ -143,6 +145,7 @@ class MedicalHistorySummaryLogicTest {
verify(ui).populateMedicalHistory(medicalHistory)
verify(ui).showDiagnosisView()
verify(ui).hideCurrentSmokerQuestion()
+ verify(ui).hideSmokelessTobaccoQuestion()
verifyNoMoreInteractions(ui)
}
@@ -158,6 +161,7 @@ class MedicalHistorySummaryLogicTest {
verify(ui).populateMedicalHistory(medicalHistory)
verify(ui).hideDiagnosisView()
verify(ui).hideCurrentSmokerQuestion()
+ verify(ui).hideSmokelessTobaccoQuestion()
verifyNoMoreInteractions(ui)
}
@@ -177,6 +181,7 @@ class MedicalHistorySummaryLogicTest {
verify(ui).populateMedicalHistory(updatedMedicalHistory)
verify(ui).showDiagnosisView()
verify(ui).hideCurrentSmokerQuestion()
+ verify(ui).hideSmokelessTobaccoQuestion()
verifyNoMoreInteractions(ui)
}
@@ -196,6 +201,7 @@ class MedicalHistorySummaryLogicTest {
verify(ui).populateMedicalHistory(updatedMedicalHistory)
verify(ui).showDiagnosisView()
verify(ui).hideCurrentSmokerQuestion()
+ verify(ui).hideSmokelessTobaccoQuestion()
verifyNoMoreInteractions(ui)
}
@@ -215,6 +221,7 @@ class MedicalHistorySummaryLogicTest {
verify(ui).populateMedicalHistory(updatedMedicalHistory)
verify(ui).showDiagnosisView()
verify(ui).hideCurrentSmokerQuestion()
+ verify(ui).hideSmokelessTobaccoQuestion()
verifyNoMoreInteractions(ui)
}
@@ -234,6 +241,7 @@ class MedicalHistorySummaryLogicTest {
verify(ui).populateMedicalHistory(updatedMedicalHistory)
verify(ui).showDiagnosisView()
verify(ui).hideCurrentSmokerQuestion()
+ verify(ui).hideSmokelessTobaccoQuestion()
verifyNoMoreInteractions(ui)
}
@@ -253,6 +261,7 @@ class MedicalHistorySummaryLogicTest {
verify(ui).populateMedicalHistory(updatedMedicalHistory)
verify(ui).showDiagnosisView()
verify(ui).hideCurrentSmokerQuestion()
+ verify(ui).hideSmokelessTobaccoQuestion()
verifyNoMoreInteractions(ui)
}
@@ -272,6 +281,7 @@ class MedicalHistorySummaryLogicTest {
verify(ui).populateMedicalHistory(updatedMedicalHistory)
verify(ui).showDiagnosisView()
verify(ui).showCurrentSmokerQuestion()
+ verify(ui).hideSmokelessTobaccoQuestion()
verifyNoMoreInteractions(ui)
}
@@ -287,6 +297,7 @@ class MedicalHistorySummaryLogicTest {
verify(ui).populateMedicalHistory(medicalHistory)
verify(ui).hideDiagnosisView()
verify(ui).hideCurrentSmokerQuestion()
+ verify(ui).hideSmokelessTobaccoQuestion()
verifyNoMoreInteractions(ui)
}
@@ -302,6 +313,39 @@ class MedicalHistorySummaryLogicTest {
verify(ui).populateMedicalHistory(medicalHistory)
verify(ui).hideDiagnosisView()
verify(ui).showCurrentSmokerQuestion()
+ verify(ui).hideSmokelessTobaccoQuestion()
+ verifyNoMoreInteractions(ui)
+ }
+
+ @Test
+ fun `when show smokeless tobacco question is disabled, then hide the show smokeless tobacco question view`() {
+ // given
+ whenever(medicalHistoryRepository.historyForPatientOrDefault(medicalHistoryUuid, patientUuid)) doReturn Observable.just(medicalHistory)
+
+ // when
+ setupController(showSmokelessTobaccoQuestion = false)
+
+ // then
+ verify(ui).populateMedicalHistory(medicalHistory)
+ verify(ui).hideDiagnosisView()
+ verify(ui).hideCurrentSmokerQuestion()
+ verify(ui).hideSmokelessTobaccoQuestion()
+ verifyNoMoreInteractions(ui)
+ }
+
+ @Test
+ fun `when show smokeless tobacco question is enabled, then show the show smokeless tobacco question view`() {
+ // given
+ whenever(medicalHistoryRepository.historyForPatientOrDefault(medicalHistoryUuid, patientUuid)) doReturn Observable.just(medicalHistory)
+
+ // when
+ setupController(showCurrentSmokerQuestion = true, showSmokelessTobaccoQuestion = true)
+
+ // then
+ verify(ui).populateMedicalHistory(medicalHistory)
+ verify(ui).hideDiagnosisView()
+ verify(ui).showCurrentSmokerQuestion()
+ verify(ui).showSmokelessTobaccoQuestion()
verifyNoMoreInteractions(ui)
}
@@ -325,6 +369,7 @@ class MedicalHistorySummaryLogicTest {
private fun setupController(
facility: Facility = facilityWithDiabetesManagementDisabled,
showCurrentSmokerQuestion: Boolean = false,
+ showSmokelessTobaccoQuestion: Boolean = false,
) {
val effectHandler = MedicalHistorySummaryEffectHandler(
schedulers = TestSchedulersProvider.trampoline(),
@@ -337,7 +382,7 @@ class MedicalHistorySummaryLogicTest {
val uiRenderer = MedicalHistorySummaryUiRenderer(ui)
testFixture = MobiusTestFixture(
events = events.ofType(),
- defaultModel = MedicalHistorySummaryModel.create(patientUuid, showCurrentSmokerQuestion),
+ defaultModel = MedicalHistorySummaryModel.create(patientUuid, showCurrentSmokerQuestion, showSmokelessTobaccoQuestion),
init = MedicalHistorySummaryInit(),
update = MedicalHistorySummaryUpdate(),
effectHandler = effectHandler.build(),
diff --git a/app/src/test/java/org/simple/clinic/summary/medicalhistory/MedicalHistorySummaryUpdateTest.kt b/app/src/test/java/org/simple/clinic/summary/medicalhistory/MedicalHistorySummaryUpdateTest.kt
index 70928282f19..7cd21d09b05 100644
--- a/app/src/test/java/org/simple/clinic/summary/medicalhistory/MedicalHistorySummaryUpdateTest.kt
+++ b/app/src/test/java/org/simple/clinic/summary/medicalhistory/MedicalHistorySummaryUpdateTest.kt
@@ -5,9 +5,9 @@ import com.spotify.mobius.test.NextMatchers.hasModel
import com.spotify.mobius.test.UpdateSpec
import com.spotify.mobius.test.UpdateSpec.assertThatNext
import org.junit.Test
+import org.simple.clinic.TestData
import org.simple.clinic.medicalhistory.Answer
import org.simple.clinic.medicalhistory.MedicalHistoryQuestion.DiagnosedWithHypertension
-import org.simple.clinic.TestData
import java.util.UUID
class MedicalHistorySummaryUpdateTest {
@@ -28,7 +28,7 @@ class MedicalHistorySummaryUpdateTest {
isSmoking = Answer.No
)
val medicalHistoryLoadedModel = MedicalHistorySummaryModel
- .create(patientUuid, true)
+ .create(patientUuid, showIsSmokingQuestion = true, showSmokelessTobaccoQuestion = true)
.medicalHistoryLoaded(medicalHistory)
val updatedMedicalHistory = medicalHistory
diff --git a/app/src/testFixtures/kotlin/org/simple/clinic/TestData.kt b/app/src/testFixtures/kotlin/org/simple/clinic/TestData.kt
index 1a6629978b8..df868f8e539 100644
--- a/app/src/testFixtures/kotlin/org/simple/clinic/TestData.kt
+++ b/app/src/testFixtures/kotlin/org/simple/clinic/TestData.kt
@@ -760,6 +760,7 @@ object TestData {
isOnDiabetesTreatment: Answer = randomMedicalHistoryAnswer(),
hasDiabetes: Answer = randomMedicalHistoryAnswer(),
isSmoking: Answer = randomMedicalHistoryAnswer(),
+ isUsingSmokelessTobacco: Answer = randomMedicalHistoryAnswer(),
cholesterol: Float? = 400f,
syncStatus: SyncStatus = randomOfEnum(SyncStatus::class),
createdAt: Instant = Instant.now(),
@@ -777,6 +778,7 @@ object TestData {
hasHadKidneyDisease = hasHadKidneyDisease,
diagnosedWithDiabetes = hasDiabetes,
isSmoking = isSmoking,
+ isUsingSmokelessTobacco = isUsingSmokelessTobacco,
cholesterol = cholesterol,
syncStatus = syncStatus,
createdAt = createdAt,
@@ -797,6 +799,7 @@ object TestData {
isOnDiabetesTreatment: Answer = randomMedicalHistoryAnswer(),
hasDiabetes: Answer = randomMedicalHistoryAnswer(),
isSmoking: Answer = randomMedicalHistoryAnswer(),
+ isUsingSmokelessTobacco: Answer = randomMedicalHistoryAnswer(),
cholesterol: Float? = 400f,
createdAt: Instant = Instant.now(),
updatedAt: Instant = Instant.now(),
@@ -814,6 +817,7 @@ object TestData {
hasDiabetes = hasDiabetes,
hasHypertension = diagnosedWithHypertension,
isSmoking = isSmoking,
+ isUsingSmokelessTobacco = isUsingSmokelessTobacco,
cholesterol = cholesterol,
createdAt = createdAt,
updatedAt = updatedAt,
diff --git a/build.gradle.kts b/build.gradle.kts
index b13ef7c9ad4..e177aa648f7 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -15,7 +15,7 @@ plugins {
buildscript {
extra.apply {
set("compileSdkVersion", 35)
- set("minSdkVersion", 21)
+ set("minSdkVersion", 26)
set("targetSdkVersion", 35)
}
diff --git a/common-ui/src/main/res/color-v23/answer_chip_background.xml b/common-ui/src/main/res/color-v23/answer_chip_background.xml
deleted file mode 100644
index 387fdcb9b37..00000000000
--- a/common-ui/src/main/res/color-v23/answer_chip_background.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/common-ui/src/main/res/color-v23/answer_chip_text.xml b/common-ui/src/main/res/color-v23/answer_chip_text.xml
deleted file mode 100644
index 710cc8549d1..00000000000
--- a/common-ui/src/main/res/color-v23/answer_chip_text.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/common-ui/src/main/res/color-v23/color_on_surface_11.xml b/common-ui/src/main/res/color-v23/color_on_surface_11.xml
deleted file mode 100644
index 12b5fe4c1ce..00000000000
--- a/common-ui/src/main/res/color-v23/color_on_surface_11.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
diff --git a/common-ui/src/main/res/color-v23/color_on_surface_34.xml b/common-ui/src/main/res/color-v23/color_on_surface_34.xml
deleted file mode 100644
index edf4740f774..00000000000
--- a/common-ui/src/main/res/color-v23/color_on_surface_34.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
diff --git a/common-ui/src/main/res/color-v23/color_on_surface_67.xml b/common-ui/src/main/res/color-v23/color_on_surface_67.xml
deleted file mode 100644
index 7616f5c03e9..00000000000
--- a/common-ui/src/main/res/color-v23/color_on_surface_67.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
diff --git a/common-ui/src/main/res/color-v23/outline_btn_stroke_color_selector.xml b/common-ui/src/main/res/color-v23/outline_btn_stroke_color_selector.xml
deleted file mode 100644
index 36684e24083..00000000000
--- a/common-ui/src/main/res/color-v23/outline_btn_stroke_color_selector.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/common-ui/src/main/res/color-v23/pin_edittext_underline.xml b/common-ui/src/main/res/color-v23/pin_edittext_underline.xml
deleted file mode 100644
index 41865e5c876..00000000000
--- a/common-ui/src/main/res/color-v23/pin_edittext_underline.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/common-ui/src/main/res/color/answer_chip_background.xml b/common-ui/src/main/res/color/answer_chip_background.xml
index 96dd6266aa1..387fdcb9b37 100644
--- a/common-ui/src/main/res/color/answer_chip_background.xml
+++ b/common-ui/src/main/res/color/answer_chip_background.xml
@@ -1,5 +1,5 @@
-
-
+
+
diff --git a/common-ui/src/main/res/color/answer_chip_text.xml b/common-ui/src/main/res/color/answer_chip_text.xml
index a6e18bbed70..710cc8549d1 100644
--- a/common-ui/src/main/res/color/answer_chip_text.xml
+++ b/common-ui/src/main/res/color/answer_chip_text.xml
@@ -1,5 +1,5 @@
-
-
+
+
diff --git a/common-ui/src/main/res/color/color_on_surface_11.xml b/common-ui/src/main/res/color/color_on_surface_11.xml
index e544123f7cc..12b5fe4c1ce 100644
--- a/common-ui/src/main/res/color/color_on_surface_11.xml
+++ b/common-ui/src/main/res/color/color_on_surface_11.xml
@@ -1,4 +1,4 @@
-
+
-
+
diff --git a/common-ui/src/main/res/color/color_on_surface_34.xml b/common-ui/src/main/res/color/color_on_surface_34.xml
index 2add02a1457..edf4740f774 100644
--- a/common-ui/src/main/res/color/color_on_surface_34.xml
+++ b/common-ui/src/main/res/color/color_on_surface_34.xml
@@ -1,4 +1,4 @@
-
+
-
+
diff --git a/common-ui/src/main/res/color/color_on_surface_67.xml b/common-ui/src/main/res/color/color_on_surface_67.xml
index b167015344f..7616f5c03e9 100644
--- a/common-ui/src/main/res/color/color_on_surface_67.xml
+++ b/common-ui/src/main/res/color/color_on_surface_67.xml
@@ -1,4 +1,4 @@
-
+
-
+
diff --git a/common-ui/src/main/res/color/outline_btn_stroke_color_selector.xml b/common-ui/src/main/res/color/outline_btn_stroke_color_selector.xml
index de37af3aa64..36684e24083 100644
--- a/common-ui/src/main/res/color/outline_btn_stroke_color_selector.xml
+++ b/common-ui/src/main/res/color/outline_btn_stroke_color_selector.xml
@@ -1,5 +1,5 @@
-
+
-
-
+
+
diff --git a/common-ui/src/main/res/color/pin_edittext_underline.xml b/common-ui/src/main/res/color/pin_edittext_underline.xml
index 120ba8605d1..41865e5c876 100644
--- a/common-ui/src/main/res/color/pin_edittext_underline.xml
+++ b/common-ui/src/main/res/color/pin_edittext_underline.xml
@@ -1,5 +1,5 @@
-
+
-
-
+
+
diff --git a/gradle.properties b/gradle.properties
index 4b65988b86c..f868c5eb73f 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -47,3 +47,7 @@ disableScreenshot=false
allowRootedDevice=true
maestroTests=false
org.gradle.unsafe.configuration-cache=true
+
+# This is a workaround for https://github.com/square/moshi/issues/1874#issuecomment-2827552256
+# which causes a KSP2 crash when using @JsonQualifier
+ksp.useKSP2=false
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 837a852be4f..996410f87a9 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,5 +1,5 @@
[versions]
-agp = "8.8.2"
+agp = "8.12.0"
androidx-cameraView = "1.4.2"
androidx-camera = "1.4.2"
@@ -12,21 +12,23 @@ androidx-lifecycle = "2.8.7"
androidx-activity = "1.10.1"
chucker = "4.2.0"
-dagger = "2.56.2"
+dagger = "2.57"
-kotlin = "2.1.20"
+kotlin = "2.2.0"
+
+ksp = "2.2.0-2.0.2"
ktlint = "0.36.0"
-lint = "31.9.2"
+lint = "31.12.0"
mobius = "2.1.1"
moshi = "1.15.2"
-okhttp = "4.12.0"
+okhttp = "5.1.0"
-retrofit = "2.11.0"
+retrofit = "3.0.0"
room-metadataGenerator = "2.1.0"
@@ -40,9 +42,7 @@ androidx-compose-bom = "2025.07.00"
composeThemeAdapter = "0.36.0"
-ksp = "2.1.20-1.0.31"
-
-sqlCipher = "4.9.0"
+sqlCipher = "4.10.0"
[libraries]
android-desugaring = "com.android.tools:desugar_jdk_libs:2.1.5"
@@ -51,13 +51,13 @@ androidx-annotation-annotation = "androidx.annotation:annotation:1.9.1"
# When bumping this dependency verify whether `org.simple.clinic.feature.Feature.ChangeLanguage`
# can be enabled again.
-androidx-appcompat = "androidx.appcompat:appcompat:1.7.0"
+androidx-appcompat = "androidx.appcompat:appcompat:1.7.1"
androidx-archCoreTesting = "androidx.arch.core:core-testing:2.2.0"
androidx-cardview = "androidx.cardview:cardview:1.0.0"
androidx-constraintlayout = "androidx.constraintlayout:constraintlayout:2.2.1"
androidx-core-ktx = "androidx.core:core-ktx:1.16.0"
-androidx-fragment = "androidx.fragment:fragment-ktx:1.8.6"
+androidx-fragment = "androidx.fragment:fragment-ktx:1.8.8"
androidx-preference = "androidx.preference:preference:1.2.1"
androidx-recyclerview = "androidx.recyclerview:recyclerview:1.4.0"
androidx-viewpager2 = "androidx.viewpager2:viewpager2:1.1.0"
@@ -94,7 +94,7 @@ androidx-viewmodel-savedstate = { module = "androidx.lifecycle:lifecycle-viewmod
androidx-lifecycle-livedata-ktx = { module = "androidx.lifecycle:lifecycle-livedata-ktx", version.ref = "androidx-lifecycle" }
-asm = "org.ow2.asm:asm:9.7.1"
+asm = "org.ow2.asm:asm:9.8"
chucker = { module = "com.github.chuckerteam.chucker:library", version.ref = "chucker" }
chucker-no-op = { module = "com.github.chuckerteam.chucker:library-no-op", version.ref = "chucker" }
@@ -107,8 +107,8 @@ edittext-pinentry = "com.alimuzaffar.lib:pinentryedittext:2.0.6"
faker = "com.github.blocoio:faker:2.0.4"
-firebase-config = "com.google.firebase:firebase-config:22.1.2"
-firebase-analytics = "com.google.firebase:firebase-analytics-ktx:22.4.0"
+firebase-config = "com.google.firebase:firebase-config:23.0.0"
+firebase-analytics = "com.google.firebase:firebase-analytics-ktx:22.5.0"
gson = "com.google.code.gson:gson:2.13.1"
@@ -116,7 +116,7 @@ itemanimators = "com.mikepenz:itemanimators:1.1.0"
itext7 = "com.itextpdf:itext7-core:7.2.5"
-jackson-core = "com.fasterxml.jackson.core:jackson-core:2.18.2"
+jackson-core = "com.fasterxml.jackson.core:jackson-core:2.19.2"
jbcrypt = "org.mindrot:jbcrypt:0.4"
@@ -138,7 +138,7 @@ lint-tests = { module = "com.android.tools.lint:lint-tests", version.ref = "lint
#noinspection GradleDependency
logback-classic = "ch.qos.logback:logback-classic:1.2.11"
-lottie = "com.airbnb.android:lottie-compose:6.6.2"
+lottie = "com.airbnb.android:lottie-compose:6.6.7"
material = "com.google.android.material:material:1.12.0"