1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * 17 */ 18 19 /** 20 * Copyright (C) 2022 The Android Open Source Project 21 * 22 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 23 * in compliance with the License. You may obtain a copy of the License at 24 * 25 * http://www.apache.org/licenses/LICENSE-2.0 26 * 27 * Unless required by applicable law or agreed to in writing, software distributed under the License 28 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 29 * or implied. See the License for the specific language governing permissions and limitations under 30 * the License. 31 */ 32 package com.android.healthconnect.controller.tests.entrydetails 33 34 import android.content.Context 35 import android.health.connect.datatypes.ExerciseCompletionGoal 36 import android.health.connect.datatypes.ExercisePerformanceGoal 37 import android.health.connect.datatypes.ExerciseRoute 38 import android.health.connect.datatypes.ExerciseSegmentType 39 import android.health.connect.datatypes.PlannedExerciseStep 40 import android.health.connect.datatypes.units.Length 41 import android.health.connect.datatypes.units.Velocity 42 import androidx.lifecycle.MutableLiveData 43 import androidx.test.espresso.Espresso.onView 44 import androidx.test.espresso.assertion.ViewAssertions.doesNotExist 45 import androidx.test.espresso.assertion.ViewAssertions.matches 46 import androidx.test.espresso.matcher.ViewMatchers.isDisplayed 47 import androidx.test.espresso.matcher.ViewMatchers.withId 48 import androidx.test.espresso.matcher.ViewMatchers.withText 49 import androidx.test.platform.app.InstrumentationRegistry 50 import com.android.healthconnect.controller.R 51 import com.android.healthconnect.controller.data.entries.FormattedEntry 52 import com.android.healthconnect.controller.data.entries.FormattedEntry.ExercisePerformanceGoalEntry 53 import com.android.healthconnect.controller.data.entries.FormattedEntry.ExerciseSessionEntry 54 import com.android.healthconnect.controller.data.entries.FormattedEntry.FormattedSectionContent 55 import com.android.healthconnect.controller.data.entries.FormattedEntry.PlannedExerciseBlockEntry 56 import com.android.healthconnect.controller.data.entries.FormattedEntry.PlannedExerciseSessionEntry 57 import com.android.healthconnect.controller.data.entries.FormattedEntry.PlannedExerciseStepEntry 58 import com.android.healthconnect.controller.data.entries.FormattedEntry.SeriesDataEntry 59 import com.android.healthconnect.controller.data.entries.FormattedEntry.SessionHeader 60 import com.android.healthconnect.controller.data.entries.FormattedEntry.SleepSessionEntry 61 import com.android.healthconnect.controller.entrydetails.DataEntryDetailsFragment 62 import com.android.healthconnect.controller.entrydetails.DataEntryDetailsViewModel 63 import com.android.healthconnect.controller.entrydetails.DataEntryDetailsViewModel.DateEntryFragmentState.Loading 64 import com.android.healthconnect.controller.entrydetails.DataEntryDetailsViewModel.DateEntryFragmentState.LoadingFailed 65 import com.android.healthconnect.controller.entrydetails.DataEntryDetailsViewModel.DateEntryFragmentState.WithData 66 import com.android.healthconnect.controller.permissions.data.HealthPermissionType.EXERCISE 67 import com.android.healthconnect.controller.permissions.data.HealthPermissionType.HEART_RATE 68 import com.android.healthconnect.controller.permissions.data.HealthPermissionType.PLANNED_EXERCISE 69 import com.android.healthconnect.controller.permissions.data.HealthPermissionType.SKIN_TEMPERATURE 70 import com.android.healthconnect.controller.permissions.data.HealthPermissionType.SLEEP 71 import com.android.healthconnect.controller.shared.DataType 72 import com.android.healthconnect.controller.tests.utils.TestData.WARSAW_ROUTE 73 import com.android.healthconnect.controller.tests.utils.getPlannedExerciseBlock 74 import com.android.healthconnect.controller.tests.utils.getPlannedExerciseStep 75 import com.android.healthconnect.controller.tests.utils.launchFragment 76 import com.android.healthconnect.controller.tests.utils.setLocale 77 import com.android.healthconnect.controller.utils.logging.DataEntriesElement 78 import com.android.healthconnect.controller.utils.logging.EntryDetailsElement 79 import com.android.healthconnect.controller.utils.logging.HealthConnectLogger 80 import com.android.healthconnect.controller.utils.logging.PageName 81 import dagger.hilt.android.testing.BindValue 82 import dagger.hilt.android.testing.HiltAndroidRule 83 import dagger.hilt.android.testing.HiltAndroidTest 84 import java.time.ZoneId 85 import java.util.Locale 86 import java.util.TimeZone 87 import org.hamcrest.Matchers.not 88 import org.junit.After 89 import org.junit.Before 90 import org.junit.Rule 91 import org.junit.Test 92 import org.mockito.Mockito.mock 93 import org.mockito.Mockito.`when` as whenever 94 import org.mockito.kotlin.atLeast 95 import org.mockito.kotlin.atMost 96 import org.mockito.kotlin.reset 97 import org.mockito.kotlin.times 98 import org.mockito.kotlin.verify 99 100 @HiltAndroidTest 101 class DataEntryDetailsFragmentTest { 102 @get:Rule val hiltRule = HiltAndroidRule(this) 103 104 @BindValue 105 val viewModel: DataEntryDetailsViewModel = mock(DataEntryDetailsViewModel::class.java) 106 private lateinit var context: Context 107 @BindValue val healthConnectLogger: HealthConnectLogger = org.mockito.kotlin.mock() 108 109 @Before setupnull110 fun setup() { 111 hiltRule.inject() 112 context = InstrumentationRegistry.getInstrumentation().context 113 context.setLocale(Locale.UK) 114 TimeZone.setDefault(TimeZone.getTimeZone(ZoneId.of("UTC"))) 115 } 116 117 @After tearDownnull118 fun tearDown() { 119 reset(healthConnectLogger) 120 } 121 122 @Test dataEntriesDetailsInit_showsFragmentnull123 fun dataEntriesDetailsInit_showsFragment() { 124 whenever(viewModel.sessionData).thenReturn(MutableLiveData(WithData(emptyList()))) 125 126 launchFragment<DataEntryDetailsFragment>( 127 DataEntryDetailsFragment.createBundle( 128 permissionType = SLEEP, entryId = "1", showDataOrigin = true)) 129 130 onView(withId(R.id.loading)).check(matches(not(isDisplayed()))) 131 } 132 133 @Test dataEntriesDetailsInit_error_showsErrornull134 fun dataEntriesDetailsInit_error_showsError() { 135 whenever(viewModel.sessionData).thenReturn(MutableLiveData(LoadingFailed)) 136 137 launchFragment<DataEntryDetailsFragment>( 138 DataEntryDetailsFragment.createBundle( 139 permissionType = SLEEP, entryId = "1", showDataOrigin = true)) 140 141 onView(withId(R.id.error_view)).check(matches(isDisplayed())) 142 } 143 144 @Test dataEntriesDetailsInit_loading_showsLoadingnull145 fun dataEntriesDetailsInit_loading_showsLoading() { 146 whenever(viewModel.sessionData).thenReturn(MutableLiveData(Loading)) 147 148 launchFragment<DataEntryDetailsFragment>( 149 DataEntryDetailsFragment.createBundle( 150 permissionType = SLEEP, entryId = "1", showDataOrigin = true)) 151 152 onView(withId(R.id.loading)).check(matches(isDisplayed())) 153 } 154 155 @Test dataEntriesDetailsInit_withData_showsItemnull156 fun dataEntriesDetailsInit_withData_showsItem() { 157 whenever(viewModel.sessionData) 158 .thenReturn( 159 MutableLiveData( 160 WithData( 161 listOf( 162 SleepSessionEntry( 163 uuid = "1", 164 header = "07:06 • TEST_APP_NAME", 165 headerA11y = "07:06 • TEST_APP_NAME", 166 title = "12 hour sleeping", 167 titleA11y = "12 hour sleeping", 168 dataType = DataType.SLEEP, 169 notes = "notes"))))) 170 171 launchFragment<DataEntryDetailsFragment>( 172 DataEntryDetailsFragment.createBundle( 173 permissionType = SLEEP, entryId = "1", showDataOrigin = true)) 174 175 onView(withText("07:06 • TEST_APP_NAME")).check(matches(isDisplayed())) 176 onView(withText("12 hour sleeping")).check(matches(isDisplayed())) 177 onView(withText("notes")).check(matches(isDisplayed())) 178 179 verify(healthConnectLogger, atLeast(1)).setPageId(PageName.ENTRY_DETAILS_PAGE) 180 verify(healthConnectLogger).logPageImpression() 181 verify(healthConnectLogger).logImpression(DataEntriesElement.SLEEP_SESSION_ENTRY_BUTTON) 182 } 183 184 @Test dataEntriesDetailsInit_withDetails_showsItem_showsDetailsnull185 fun dataEntriesDetailsInit_withDetails_showsItem_showsDetails() { 186 val list = buildList { 187 add(getFormattedSleepSession()) 188 addAll(getSleepStages()) 189 } 190 whenever(viewModel.sessionData).thenReturn(MutableLiveData(WithData(list))) 191 192 launchFragment<DataEntryDetailsFragment>( 193 DataEntryDetailsFragment.createBundle( 194 permissionType = SLEEP, entryId = "1", showDataOrigin = true)) 195 196 onView(withText("12 hour sleeping")).check(matches(isDisplayed())) 197 onView(withText("6 hour light sleeping")).check(matches(isDisplayed())) 198 onView(withText("6 hour deep sleeping")).check(matches(isDisplayed())) 199 } 200 201 @Test dataEntriesDetailsInit_withHeartRate_showsItem_showsDetailsnull202 fun dataEntriesDetailsInit_withHeartRate_showsItem_showsDetails() { 203 val list = buildList { add(getFormattedSeriesData()) } 204 205 whenever(viewModel.sessionData).thenReturn(MutableLiveData(WithData(list))) 206 207 launchFragment<DataEntryDetailsFragment>( 208 DataEntryDetailsFragment.createBundle( 209 permissionType = HEART_RATE, entryId = "1", showDataOrigin = true)) 210 211 onView(withText("07:06 - 8:06 • TEST_APP_NAME")).check(matches(isDisplayed())) 212 onView(withText("100 bpm")).check(matches(isDisplayed())) 213 verify(healthConnectLogger).logImpression(DataEntriesElement.DATA_ENTRY_VIEW) 214 } 215 216 @Test dataEntriesInit_withSkinTemperature_showsItem_showsDetailsnull217 fun dataEntriesInit_withSkinTemperature_showsItem_showsDetails() { 218 val list = buildList { 219 add(getSkinTemperatureEntry()) 220 addAll(getSkinTemperatureDeltas(includeBaseline = true, includeLocation = true)) 221 } 222 whenever(viewModel.sessionData).thenReturn(MutableLiveData(WithData(list))) 223 224 launchFragment<DataEntryDetailsFragment>( 225 DataEntryDetailsFragment.createBundle( 226 permissionType = SKIN_TEMPERATURE, entryId = "1", showDataOrigin = true)) 227 228 onView(withText("+0.5℃ (avg variation)")).check(matches(isDisplayed())) 229 onView(withText("Measurement location")).check(matches(isDisplayed())) 230 onView(withText("Toe")).check(matches(isDisplayed())) 231 onView(withText("Baseline")).check(matches(isDisplayed())) 232 onView(withText("25℃")).check(matches(isDisplayed())) 233 onView(withText("Variation from baseline")).check(matches(isDisplayed())) 234 verify(healthConnectLogger, times(2)) 235 .logImpression((EntryDetailsElement.REVERSE_SESSION_DETAIL_ENTRY_VIEW)) 236 verify(healthConnectLogger) 237 .logImpression((EntryDetailsElement.FORMATTED_SECTION_TITLE_VIEW)) 238 } 239 240 @Test dataEntriesInit_withSkinTemperature_locationUnknown_hidesLocationDetailnull241 fun dataEntriesInit_withSkinTemperature_locationUnknown_hidesLocationDetail() { 242 val list = buildList { 243 add(getSkinTemperatureEntry()) 244 addAll(getSkinTemperatureDeltas(includeBaseline = true, includeLocation = false)) 245 } 246 whenever(viewModel.sessionData).thenReturn(MutableLiveData(WithData(list))) 247 248 launchFragment<DataEntryDetailsFragment>( 249 DataEntryDetailsFragment.createBundle( 250 permissionType = SKIN_TEMPERATURE, entryId = "1", showDataOrigin = true)) 251 252 onView(withText("+0.5℃ (avg variation)")).check(matches(isDisplayed())) 253 onView(withText("Measurement location")).check(doesNotExist()) 254 onView(withText("Toe")).check(doesNotExist()) 255 onView(withText("Baseline")).check(matches(isDisplayed())) 256 onView(withText("25℃")).check(matches(isDisplayed())) 257 onView(withText("Variation from baseline")).check(matches(isDisplayed())) 258 verify(healthConnectLogger, atMost(1)) 259 .logImpression((EntryDetailsElement.REVERSE_SESSION_DETAIL_ENTRY_VIEW)) 260 verify(healthConnectLogger) 261 .logImpression((EntryDetailsElement.FORMATTED_SECTION_TITLE_VIEW)) 262 } 263 264 @Test dataEntriesInit_withSkinTemperature_baselineUnknown_hidesBaselineDetailnull265 fun dataEntriesInit_withSkinTemperature_baselineUnknown_hidesBaselineDetail() { 266 val list = buildList { 267 add(getSkinTemperatureEntry()) 268 addAll(getSkinTemperatureDeltas(includeBaseline = false, includeLocation = true)) 269 } 270 whenever(viewModel.sessionData).thenReturn(MutableLiveData(WithData(list))) 271 272 launchFragment<DataEntryDetailsFragment>( 273 DataEntryDetailsFragment.createBundle( 274 permissionType = SKIN_TEMPERATURE, entryId = "1", showDataOrigin = true)) 275 276 onView(withText("+0.5℃ (avg variation)")).check(matches(isDisplayed())) 277 onView(withText("Measurement location")).check(matches(isDisplayed())) 278 onView(withText("Toe")).check(matches(isDisplayed())) 279 onView(withText("Baseline")).check(doesNotExist()) 280 onView(withText("25℃")).check(doesNotExist()) 281 onView(withText("Variation from baseline")).check(matches(isDisplayed())) 282 verify(healthConnectLogger, atMost(1)) 283 .logImpression((EntryDetailsElement.REVERSE_SESSION_DETAIL_ENTRY_VIEW)) 284 verify(healthConnectLogger) 285 .logImpression((EntryDetailsElement.FORMATTED_SECTION_TITLE_VIEW)) 286 } 287 288 @Test dataEntriesDetailsInit_withRouteDetails_showsMapViewnull289 fun dataEntriesDetailsInit_withRouteDetails_showsMapView() { 290 val list = buildList { add(getFormattedExerciseSession(showSession = true)) } 291 whenever(viewModel.sessionData).thenReturn(MutableLiveData(WithData(list))) 292 launchFragment<DataEntryDetailsFragment>( 293 DataEntryDetailsFragment.createBundle( 294 permissionType = EXERCISE, entryId = "1", showDataOrigin = true)) 295 296 onView(withText("12 hour running")).check(matches(isDisplayed())) 297 onView(withId(R.id.map_view)).check(matches(isDisplayed())) 298 verify(healthConnectLogger).logImpression(DataEntriesElement.EXERCISE_SESSION_ENTRY_BUTTON) 299 verify(healthConnectLogger).logImpression(DataEntriesElement.EXERCISE_SESSION_MAP_VIEW) 300 } 301 302 @Test dataEntriesDetailsInit_noRouteDetails_hidesMapViewnull303 fun dataEntriesDetailsInit_noRouteDetails_hidesMapView() { 304 val list = buildList { add(getFormattedExerciseSession(showSession = false)) } 305 whenever(viewModel.sessionData).thenReturn(MutableLiveData(WithData(list))) 306 launchFragment<DataEntryDetailsFragment>( 307 DataEntryDetailsFragment.createBundle( 308 permissionType = EXERCISE, entryId = "1", showDataOrigin = true)) 309 310 onView(withText("12 hour running")).check(matches(isDisplayed())) 311 onView(withId(R.id.map_view)).check(matches(not(isDisplayed()))) 312 } 313 314 @Test dataEntriesDetailsInit_withPlannedExerciseSession_showsItem_showsDetailsnull315 fun dataEntriesDetailsInit_withPlannedExerciseSession_showsItem_showsDetails() { 316 val list = buildList { 317 add( 318 PlannedExerciseSessionEntry( 319 uuid = "test_id", 320 header = "07:06 - 08:06 • Health Connect test app", 321 headerA11y = "from 07:06 to 08:06 • Health Connect test app", 322 title = "Running • Morning Run", 323 titleA11y = "Running • Morning Run", 324 dataType = DataType.PLANNED_EXERCISE, 325 notes = "Morning quick run by the park")) 326 add( 327 PlannedExerciseBlockEntry( 328 block = 329 getPlannedExerciseBlock( 330 1, 331 "Warm up", 332 listOf( 333 getPlannedExerciseStep( 334 exerciseSegmentType = 335 ExerciseSegmentType.EXERCISE_SEGMENT_TYPE_RUNNING, 336 completionGoal = 337 ExerciseCompletionGoal.DistanceGoal( 338 Length.fromMeters(1000.0)), 339 performanceGoals = 340 listOf( 341 ExercisePerformanceGoal.HeartRateGoal(100, 150), 342 ExercisePerformanceGoal.SpeedGoal( 343 Velocity.fromMetersPerSecond(25.0), 344 Velocity.fromMetersPerSecond(15.0)))))), 345 title = "Warm up: 1 time", 346 titleA11y = "Warm up 1 time")) 347 add(SessionHeader("Notes")) 348 add(FormattedSectionContent("Morning quick run by the park")) 349 add( 350 PlannedExerciseStepEntry( 351 step = 352 getPlannedExerciseStepBuilder() 353 .setPerformanceGoals( 354 listOf( 355 ExercisePerformanceGoal.HeartRateGoal(150, 180), 356 ExercisePerformanceGoal.SpeedGoal( 357 Velocity.fromMetersPerSecond(25.0), 358 Velocity.fromMetersPerSecond(15.0)))) 359 .build(), 360 title = "4 km Running", 361 titleA11y = "4 kilometres Running")) 362 add(FormattedSectionContent(title = "This is a test exercise step", bulleted = true)) 363 add( 364 ExercisePerformanceGoalEntry( 365 goal = ExercisePerformanceGoal.HeartRateGoal(150, 180), 366 title = "150 bpm - 180 bpm", 367 titleA11y = "150 beats per minute - 180 beats per minute")) 368 add( 369 ExercisePerformanceGoalEntry( 370 goal = 371 ExercisePerformanceGoal.SpeedGoal( 372 Velocity.fromMetersPerSecond(180.0), 373 Velocity.fromMetersPerSecond(90.0)), 374 title = "180 km/h - 90 km/h", 375 titleA11y = "180 kilometres per hour - 90 kilometres per hour")) 376 } 377 378 whenever(viewModel.sessionData).thenReturn(MutableLiveData(WithData(list))) 379 380 launchFragment<DataEntryDetailsFragment>( 381 DataEntryDetailsFragment.createBundle( 382 permissionType = PLANNED_EXERCISE, entryId = "1", showDataOrigin = true)) 383 384 onView(withText("07:06 - 08:06 • Health Connect test app")).check(matches(isDisplayed())) 385 onView(withText("Running • Morning Run")).check(matches(isDisplayed())) 386 onView(withText("Notes")).check(matches(isDisplayed())) 387 onView(withText("Morning quick run by the park")).check(matches(isDisplayed())) 388 onView(withText("Warm up: 1 time")).check(matches(isDisplayed())) 389 onView(withText("4 km Running")).check(matches(isDisplayed())) 390 onView(withText("• This is a test exercise step")).check(matches(isDisplayed())) 391 onView(withText("• 150 bpm - 180 bpm")).check(matches(isDisplayed())) 392 onView(withText("• 180 km/h - 90 km/h")).check(matches(isDisplayed())) 393 verify(healthConnectLogger, times(2)) 394 .logImpression((EntryDetailsElement.FORMATTED_SECTION_CONTENT_VIEW)) 395 verify(healthConnectLogger) 396 .logImpression((EntryDetailsElement.PLANNED_EXERCISE_BLOCK_ENTRY_VIEW)) 397 verify(healthConnectLogger) 398 .logImpression((EntryDetailsElement.PLANNED_EXERCISE_STEP_ENTRY_VIEW)) 399 verify(healthConnectLogger).logImpression((EntryDetailsElement.SESSION_DETAIL_HEADER_VIEW)) 400 } 401 getSleepStagesnull402 private fun getSleepStages(): List<FormattedEntry> { 403 return listOf( 404 FormattedEntry.SessionHeader(header = "Stages"), 405 FormattedEntry.FormattedSessionDetail( 406 uuid = "1", 407 header = "07:06 • TEST_APP_NAME", 408 headerA11y = "07:06 • TEST_APP_NAME", 409 title = "6 hour light sleeping", 410 titleA11y = "6 hour light sleeping", 411 ), 412 FormattedEntry.FormattedSessionDetail( 413 uuid = "1", 414 header = "07:06 • TEST_APP_NAME", 415 headerA11y = "07:06 • TEST_APP_NAME", 416 title = "6 hour deep sleeping", 417 titleA11y = "6 hour deep sleeping", 418 )) 419 } 420 getSkinTemperatureEntrynull421 private fun getSkinTemperatureEntry(): FormattedEntry { 422 return SeriesDataEntry( 423 uuid = "1", 424 header = "16:00 - 17:00 • TEST_APP_NAME", 425 headerA11y = "16:00 - 17:00 • TEST_APP_NAME", 426 title = "+0.5℃ (avg variation)", 427 titleA11y = "+0.5 degrees Celsius (average variation)", 428 dataType = DataType.SKIN_TEMPERATURE) 429 } 430 getSkinTemperatureDeltasnull431 private fun getSkinTemperatureDeltas( 432 includeBaseline: Boolean, 433 includeLocation: Boolean 434 ): List<FormattedEntry> { 435 val locationDefinedFormattedEntry: FormattedEntry.ReverseSessionDetail = 436 FormattedEntry.ReverseSessionDetail( 437 uuid = "1", 438 header = "Measurement location", 439 headerA11y = "Measurement location", 440 title = "Toe", 441 titleA11y = "Toe", 442 ) 443 444 val baselineDefinedFormattedEntry: FormattedEntry.ReverseSessionDetail = 445 FormattedEntry.ReverseSessionDetail( 446 uuid = "1", 447 header = "Baseline", 448 headerA11y = "Baseline", 449 title = "25℃", 450 titleA11y = "25 degrees Celsius", 451 ) 452 453 val deltaFormattedEntries: List<FormattedEntry> = 454 listOf( 455 FormattedEntry.FormattedSectionTitle(title = "Variation from baseline"), 456 FormattedEntry.FormattedSessionDetail( 457 uuid = "1", 458 header = "16:10 AM", 459 headerA11y = "16:10 AM", 460 title = "+1.5℃", 461 titleA11y = "+1.5 degrees Celsius", 462 ), 463 FormattedEntry.FormattedSessionDetail( 464 uuid = "1", 465 header = "16:40 AM", 466 headerA11y = "16:40 AM", 467 title = "-0.5℃", 468 titleA11y = "-0.5 degrees Celsius", 469 )) 470 471 return if (includeBaseline && includeLocation) { 472 listOf(locationDefinedFormattedEntry, baselineDefinedFormattedEntry) 473 .plus(deltaFormattedEntries) 474 } else if (includeBaseline) { 475 listOf(baselineDefinedFormattedEntry).plus(deltaFormattedEntries) 476 } else if (includeLocation) { 477 listOf(locationDefinedFormattedEntry).plus(deltaFormattedEntries) 478 } else { 479 deltaFormattedEntries 480 } 481 } 482 getFormattedSleepSessionnull483 private fun getFormattedSleepSession(): SleepSessionEntry { 484 return SleepSessionEntry( 485 uuid = "1", 486 header = "07:06 • TEST_APP_NAME", 487 headerA11y = "07:06 • TEST_APP_NAME", 488 title = "12 hour sleeping", 489 titleA11y = "12 hour sleeping", 490 dataType = DataType.SLEEP, 491 notes = "notes") 492 } 493 getFormattedExerciseSessionnull494 private fun getFormattedExerciseSession(showSession: Boolean): ExerciseSessionEntry { 495 return ExerciseSessionEntry( 496 uuid = "1", 497 header = "07:06 • TEST_APP_NAME", 498 headerA11y = "07:06 • TEST_APP_NAME", 499 title = "12 hour running", 500 titleA11y = "12 hour running", 501 dataType = DataType.EXERCISE, 502 notes = "notes", 503 route = 504 if (showSession) { 505 ExerciseRoute(WARSAW_ROUTE) 506 } else { 507 null 508 }) 509 } 510 getFormattedSeriesDatanull511 private fun getFormattedSeriesData(): SeriesDataEntry { 512 return SeriesDataEntry( 513 uuid = "1", 514 header = "07:06 - 8:06 • TEST_APP_NAME", 515 headerA11y = "07:06 - 8:06 • TEST_APP_NAME", 516 title = "100 bpm", 517 titleA11y = "100 beats per minute", 518 dataType = DataType.HEART_RATE) 519 } 520 getPlannedExerciseStepBuildernull521 private fun getPlannedExerciseStepBuilder(): PlannedExerciseStep.Builder { 522 return PlannedExerciseStep.Builder( 523 ExerciseSegmentType.EXERCISE_SEGMENT_TYPE_RUNNING, 524 PlannedExerciseStep.EXERCISE_CATEGORY_ACTIVE, 525 ExerciseCompletionGoal.DistanceGoal(Length.fromMeters(1000.0))) 526 } 527 } 528