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 }