1 /** <lambda>null2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * ``` 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * ``` 10 * 11 * Unless required by applicable law or agreed to in writing, software distributed under the License 12 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 * or implied. See the License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 package com.android.healthconnect.testapps.toolbox.seed 17 18 import android.content.Context 19 import android.health.connect.HealthConnectManager 20 import android.health.connect.datatypes.CervicalMucusRecord 21 import android.health.connect.datatypes.CervicalMucusRecord.CervicalMucusAppearance 22 import android.health.connect.datatypes.CervicalMucusRecord.CervicalMucusSensation 23 import android.health.connect.datatypes.DataOrigin 24 import android.health.connect.datatypes.Device 25 import android.health.connect.datatypes.IntermenstrualBleedingRecord 26 import android.health.connect.datatypes.MenstruationFlowRecord 27 import android.health.connect.datatypes.MenstruationFlowRecord.MenstruationFlowType 28 import android.health.connect.datatypes.MenstruationPeriodRecord 29 import android.health.connect.datatypes.Metadata 30 import android.health.connect.datatypes.OvulationTestRecord 31 import android.health.connect.datatypes.OvulationTestRecord.OvulationTestResult 32 import android.health.connect.datatypes.SexualActivityRecord 33 import android.health.connect.datatypes.SexualActivityRecord.SexualActivityProtectionUsed 34 import android.os.Build.MANUFACTURER 35 import android.os.Build.MODEL 36 import com.android.healthconnect.testapps.toolbox.utils.GeneralUtils.Companion.getMetaData 37 import com.android.healthconnect.testapps.toolbox.utils.GeneralUtils.Companion.insertRecords 38 import kotlinx.coroutines.runBlocking 39 import java.time.Duration.ofDays 40 import java.time.Duration.ofMinutes 41 import java.time.Instant 42 import java.time.temporal.ChronoUnit 43 44 class SeedCycleTrackingData(private val context: Context, private val manager: HealthConnectManager) { 45 46 companion object { 47 val VALID_CERVICAL_MUCUS_SENSATION = 48 setOf( 49 CervicalMucusSensation.SENSATION_LIGHT, 50 CervicalMucusSensation.SENSATION_MEDIUM, 51 CervicalMucusSensation.SENSATION_HEAVY, 52 CervicalMucusSensation.SENSATION_UNKNOWN 53 ) 54 val VALID_CERVICAL_MUCUS_APPEARANCE = 55 setOf( 56 CervicalMucusAppearance.APPEARANCE_DRY, 57 CervicalMucusAppearance.APPEARANCE_CREAMY, 58 CervicalMucusAppearance.APPEARANCE_STICKY, 59 CervicalMucusAppearance.APPEARANCE_WATERY, 60 CervicalMucusAppearance.APPEARANCE_EGG_WHITE, 61 CervicalMucusAppearance.APPEARANCE_UNUSUAL, 62 CervicalMucusAppearance.APPEARANCE_UNKNOWN 63 ) 64 val VALID_MENSTRUATION_FLOW_TYPE = 65 setOf( 66 MenstruationFlowType.FLOW_LIGHT, 67 MenstruationFlowType.FLOW_MEDIUM, 68 MenstruationFlowType.FLOW_HEAVY, 69 MenstruationFlowType.FLOW_UNKNOWN 70 ) 71 val VALID_OVULATION_TEST_RESULT = 72 setOf( 73 OvulationTestResult.RESULT_NEGATIVE, 74 OvulationTestResult.RESULT_POSITIVE, 75 OvulationTestResult.RESULT_HIGH, 76 OvulationTestResult.RESULT_INCONCLUSIVE 77 ) 78 val VALID_SEXUAL_ACTIVITY_PROTECTION_USED = 79 setOf( 80 SexualActivityProtectionUsed.PROTECTION_USED_UNKNOWN, 81 SexualActivityProtectionUsed.PROTECTION_USED_PROTECTED, 82 SexualActivityProtectionUsed.PROTECTION_USED_UNPROTECTED 83 ) 84 } 85 86 private val start = Instant.now().truncatedTo(ChronoUnit.DAYS) 87 private val yesterday = start.minus(ofDays(1)) 88 private val lastWeek = start.minus(ofDays(7)) 89 private val lastMonth = start.minus(ofDays(31)) 90 91 fun seedCycleTrackingData(){ 92 runBlocking { 93 try { 94 seedAllMenstruationData() 95 seedCervicalMucusRecord() 96 seedOvulationTestRecord() 97 seedSexualActivityRecord() 98 seedIntermenstrualBleedingRecord() 99 } catch (ex: Exception) { 100 throw ex 101 } 102 } 103 } 104 105 private suspend fun seedAllMenstruationData(){ 106 val todayPeriodRecord = 107 getMenstruationPeriodRecord( 108 start.minus(ofDays(5L)), 109 start) 110 val lastWeekPeriodRecord = 111 getMenstruationPeriodRecord( 112 lastWeek.minus(ofDays(1L)), 113 lastWeek.plus(ofDays(4))) 114 val lastMonthPeriodRecord = 115 getMenstruationPeriodRecord( 116 lastMonth.minus(ofDays(1L)), 117 lastMonth.plus(ofDays(10L))) 118 119 val todayFlowRecords = 120 (-5..0).map { days -> 121 getMenstruationFlowRecord(start.plus(ofDays(days.toLong()))) 122 } 123 val lastWeekFlowRecords = 124 (-1..4).map { days -> 125 getMenstruationFlowRecord(lastWeek.plus(ofDays(days.toLong()))) 126 } 127 val lastMonthFlowRecords = 128 (-1..10).map { days -> 129 getMenstruationFlowRecord(lastMonth.plus(ofDays(days.toLong()))) 130 } 131 132 insertRecords( 133 buildList { 134 add(todayPeriodRecord) 135 addAll(todayFlowRecords) 136 }, 137 manager) 138 insertRecords( 139 buildList { 140 add(lastWeekPeriodRecord) 141 addAll(lastWeekFlowRecords) 142 }, 143 manager) 144 insertRecords( 145 buildList { 146 add(lastMonthPeriodRecord) 147 addAll(lastMonthFlowRecords) 148 }, 149 manager 150 ) 151 } 152 153 private suspend fun seedCervicalMucusRecord(){ 154 val records = (1L..3).map { timeOffSet -> 155 getCervicalMucusRecord(start.plus(ofMinutes(timeOffSet))) 156 } 157 val yesterdayRecords = (1L..3).map { timeOffSet -> 158 getCervicalMucusRecord(yesterday.plus(ofMinutes(timeOffSet))) 159 } 160 val lastWeekRecords = (1L..3).map { timeOffSet -> 161 getCervicalMucusRecord(lastWeek.plus(ofMinutes(timeOffSet))) 162 } 163 val lastMonthRecords = (1L..3).map { timeOffSet -> 164 getCervicalMucusRecord(lastMonth.plus(ofMinutes(timeOffSet))) 165 } 166 167 insertRecords(records, manager) 168 insertRecords(yesterdayRecords, manager) 169 insertRecords(lastWeekRecords, manager) 170 insertRecords(lastMonthRecords, manager) 171 } 172 173 private suspend fun seedOvulationTestRecord(){ 174 val records = (1L..3).map { timeOffSet -> 175 getOvulationTestRecord(start.plus(ofMinutes(timeOffSet))) 176 } 177 val yesterdayRecords = (1L..3).map { timeOffSet -> 178 getOvulationTestRecord(yesterday.plus(ofMinutes(timeOffSet))) 179 } 180 val lastWeekRecords = (1L..3).map { timeOffSet -> 181 getOvulationTestRecord(lastWeek.plus(ofMinutes(timeOffSet))) 182 } 183 val lastMonthRecords = (1L..3).map { timeOffSet -> 184 getOvulationTestRecord(lastMonth.plus(ofMinutes(timeOffSet))) 185 } 186 187 insertRecords(records, manager) 188 insertRecords(yesterdayRecords, manager) 189 insertRecords(lastWeekRecords, manager) 190 insertRecords(lastMonthRecords, manager) 191 } 192 193 private suspend fun seedSexualActivityRecord() { 194 val records = (1L..3).map { timeOffSet -> 195 getSexualActivityRecord(start.plus(ofMinutes(timeOffSet))) 196 } 197 val yesterdayRecords = (1L..3).map { timeOffSet -> 198 getSexualActivityRecord(yesterday.plus(ofMinutes(timeOffSet))) 199 } 200 val lastWeekRecords = (1L..3).map { timeOffSet -> 201 getSexualActivityRecord(lastWeek.plus(ofMinutes(timeOffSet))) 202 } 203 val lastMonthRecords = (1L..3).map { timeOffSet -> 204 getSexualActivityRecord(lastMonth.plus(ofMinutes(timeOffSet))) 205 } 206 207 insertRecords(records, manager) 208 insertRecords(yesterdayRecords, manager) 209 insertRecords(lastWeekRecords, manager) 210 insertRecords(lastMonthRecords, manager) 211 } 212 213 private suspend fun seedIntermenstrualBleedingRecord(){ 214 val records = (1L..3).map { timeOffSet -> 215 getIntermenstrualBleedingRecord(start.plus(ofMinutes(timeOffSet))) 216 } 217 val yesterdayRecords = (1L..3).map { timeOffSet -> 218 getIntermenstrualBleedingRecord(yesterday.plus(ofMinutes(timeOffSet))) 219 } 220 val lastWeekRecords = (1L..3).map { timeOffSet -> 221 getIntermenstrualBleedingRecord(lastWeek.plus(ofMinutes(timeOffSet))) 222 } 223 val lastMonthRecords = (1L..3).map { timeOffSet -> 224 getIntermenstrualBleedingRecord(lastMonth.plus(ofMinutes(timeOffSet))) 225 } 226 227 insertRecords(records, manager) 228 insertRecords(yesterdayRecords, manager) 229 insertRecords(lastWeekRecords, manager) 230 insertRecords(lastMonthRecords, manager) 231 } 232 233 private fun getMenstruationPeriodRecord(start: Instant, end:Instant): MenstruationPeriodRecord{ 234 return MenstruationPeriodRecord.Builder(getMetaData(context), start, end).build() 235 } 236 237 private fun getMenstruationFlowRecord(time: Instant): MenstruationFlowRecord{ 238 return MenstruationFlowRecord.Builder(getMetaData(context), time, VALID_MENSTRUATION_FLOW_TYPE.random()).build() 239 } 240 241 private fun getCervicalMucusRecord(time: Instant): CervicalMucusRecord { 242 return CervicalMucusRecord.Builder( 243 getMetaData(context), 244 time, 245 VALID_CERVICAL_MUCUS_SENSATION.random(), 246 VALID_CERVICAL_MUCUS_APPEARANCE.random() 247 ).build() 248 } 249 250 private fun getOvulationTestRecord(time: Instant): OvulationTestRecord { 251 return OvulationTestRecord.Builder(getMetaData(context), time, VALID_OVULATION_TEST_RESULT.random()).build() 252 } 253 254 private fun getSexualActivityRecord(time: Instant): SexualActivityRecord { 255 return SexualActivityRecord.Builder(getMetaData(context), time, VALID_SEXUAL_ACTIVITY_PROTECTION_USED.random()).build() 256 } 257 258 private fun getIntermenstrualBleedingRecord(time: Instant): IntermenstrualBleedingRecord { 259 return IntermenstrualBleedingRecord.Builder(getMetaData(context), time).build() 260 } 261 }