Upgrade to Pro — share decks privately, control downloads, hide ads and more …

2022年ヘルスケアアプリのつくり方 / 2022 A Healthcare App Odyssey

gyamoto
October 06, 2022

2022年ヘルスケアアプリのつくり方 / 2022 A Healthcare App Odyssey

DroidKaigi2022 Day.2 Room:Cards 14:45-15:10
https://droidkaigi.jp/2022/timetable/365018

gyamoto

October 06, 2022
Tweet

More Decks by gyamoto

Other Decks in Technology

Transcript

  1. ࣗݾ঺հ Kohei Yamamoto farundorl gyamoto Android application engineer Mobility Technologies

    (2021.08 - , Main job) Upmind (2022.01 - , Side job) 2 Name : Twitter : GitHub : Job :
  2. ϔϧεέΞΞϓϦ͕ѻ͏σʔλ ϔϧεέΞΞϓϦͰ͸Ϣʔβͷ݈߁ʹؔ͢Δ༷ʑͳσʔλ͕ѻΘΕ͍ͯΔɻ ϔϧεέΞΞϓϦ͕ѻ͏σʔλͷྫ • Ϣʔβࢦඪ → ਎௕ɺମॏɺ৺ഥ਺ɺମԹɺମࢷ๱཰ɺݺٵ਺ɺͳͲ • ਭ຾ →

    ਭ຾࣌ؒɺ࣌ؒ͋ͨΓͷਭ຾ͷਂ͞ • ϥϯχϯά → ӡಈ࣌ؒɺ࣌ؒ͋ͨΓͷ଎౓ • ΤΫααΠζ → ࣮ࢪͨ͠ӡಈɺӡಈ࣌ؒɺ࣌ؒ͋ͨΓͷ৺ഥ਺ 8
  3. ݖݶ੍ޚ HealthConnect σʔλछผຖͷΞΫηεݖ ʢಡΈऔΓɺॻ͖ࠐΈʣͷݖݶ੍ޚ → ࣮૷͸ෳࡶ
 → Ϣʔβͱͯ͠خ͍͠ 15 GoogleFit

    ΞϓϦ୯ҐͷΞΫηεݖʢෳ਺σʔλ छผΛ·ͱΊͨʣͷOAuthݖݶ੍ޚ → ࣮૷͸γϯϓϧ
 → ΤϯδχΞͱͯ͠خ͍͠ HealthConnectͱGoogleFitͷൺֱ
  4. ҆ఆੑͱকདྷੑ HealthConnect ΞϧϑΝ൛
 طଘϓϥοτϑΥʔϜ͸ରԠ༧ఆ → ·ͩAPIʹഁյతมߋ͸͋Γͦ͏
 → GoogleFitҎ֎ͷϓϥοτϑΥʔϜ
 ɹͱ࿈ܞͰ͖Δʂʂ 17

    GoogleFit 2022೥5݄͔Βඇਪ঑
 2024೥ʹαϙʔτऴྃ༧ఆ → ಈ࡞͸҆ఆ͍ͯ͠Δ
 → ৽͘͠ಋೖ͢Δͷ͸ආ͚͍ͨ HealthConnectͱGoogleFitͷൺֱ
  5. HealthConnectͱGoogleFitͷൺֱ 18 HealthConnect GoogleFit ɹσʔλ؅ཧ ̋ σόΠεத৺ ˕ αʔόத৺ ɹݖݶ੍ޚ

    ˕ σʔλͱΞΫηεݖ୯Ґ ˚ ΞϓϦ୯Ґ ɹOSόʔδϣϯ ˚ Android 9.0~ ̋ Android 4.1~ ɹ҆ఆੑ ˚ ·ͩΞϧϑΝ൛ ̋ ҆ఆ൛ ɹকདྷੑ ˕ طଘϓϥοτϑΥʔϜ͕ରԠ༧ఆ ˚ Android޲͚API͸ඇਪ঑
  6. UpmindͰѻ͏σʔλͷϑϩʔΛߟ͑Δ σʔλͷ׆༻
 
 վળίϯςϯπʢᛉ૝ɺ ϤΨɺετϨονʣ 25 σʔλͷอଘ
 
 ΞϓϦαʔόʹࣗ཯ਆ ܦείΞΛอଘ

    ϓϥοτϑΥʔϜ࿈ܞ
 
 า਺ɺਭ຾ɺফඅΧϩ ϦʔΛऔಘ σʔλͷೖྗ
 
 ࣗ཯ਆܦείΞʢ৺ഥͷ ΏΒ͗ɺ৺ഥ਺ʣΛܭଌ ϓϥοτϑΥʔϜ࿈ܞ
 
 ৺ഥ਺ͷॻ͖ࠐΈ
  7. UpmindͰѻ͏σʔλͷϑϩʔΛߟ͑Δ σʔλͷ׆༻
 
 վળίϯςϯπʢᛉ૝ɺ ϤΨɺετϨονʣ 26 Ϣʔβମݧ
 
 վળίϯςϯπΛࢹ ௌɺମݧ

    ϓϥοτϑΥʔϜ࿈ܞ
 
 վળίϯςϯπͷ࣮ࢪ Λॻ͖ࠐΈ σʔλͷอଘ
 
 ΞϓϦαʔόʹվળίϯ ςϯπͷࢹௌ׬ྃΛอଘ
  8. HealthConnectͷಋೖ AndroidStudio : HealthConnect : UI impl : Async impl

    : Gradle script : 28 Dolphin | 2021.3.1 1.0.0-alpha05 Jetpack Compose 1.2.1 Kotlin Coroutines 1.6.0 Groovy AndroidΞϓϦ͔ΒHealthConnectͷݖݶऔಘͱσʔλͷಡΈॻ͖Λ࣮૷͢Δɻ ಈ࡞؀ڥͱόʔδϣϯ
  9. I. HealthConnectͷϥΠϒϥϦಋೖͱϓϩδΣΫτઃఆ 2. AndroidManifest.xml ʹHealthConnectͷݖݶ࢖༻ํ๏Λઆ໌͢ΔActivityΛ௥Ճ ͢Δ 31 <activity android:name=".PermissionsRationaleActivity"> <intent-filter>

    <action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" /> </intent-filter> <!-- TODO: σʔλछผͱΞΫηεݖΛ health_permission.xml ʹఆٛ͢Δ --> <meta-data android:name="health_permissions" android:resource="@array/health_permissions" />
  10. II. HealthConnectͷσʔλछผΛܾΊΔ 1. iOSͷHealthKitͱಉ༷ͷσʔλछผΛHealthConnectͰݕ౼͢Δ 33 let writeTypes = Set([ HKObjectType.quantityType(forIdentifier:

    HKQuantityTypeIdentifier.heartRate)!, HKObjectType.categoryType(forIdentifier: HKCategoryTypeIdentifier.mindfulSession)! ]) let readTypes = Set([ HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount)!, HKObjectType.categoryType(forIdentifier: HKCategoryTypeIdentifier.sleepAnalysis)!, HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.activeEnergyBurned)! ])
  11. II. HealthConnectͷσʔλछผΛܾΊΔ 2. HealthConnectͷσʔλछผʢRecordΠϯλʔϑΣʔεʣʹఆٛ͞Εͨ
 ৺ഥɺϚΠϯυϑϧωεɺา਺ɺਭ຾ɺΤωϧΪʔফඅྔͷΫϥεΛ୳͢… 34 ActiveCaloriesBurnedRecord, BasalBodyTemperatureRecord, BasalMetabolicRateRecord, BloodGlucoseRecord,

    BloodPressureRecord, BodyFatRecord, BodyTemperatureRecord, BoneMassRecord, CervicalMucusRecord, CyclingPedalingCadenceRecord, DistanceRecord, ElevationGainedRecord, ExerciseEventRecord, ExerciseLapRecord, ExerciseRepetitionsRecord, ExerciseSessionRecord, FloorsClimbedRecord, HeartRateRecord, HeightRecord, HipCircumferenceRecord, HydrationRecord, LeanBodyMassRecord, MenstruationFlowRecord, NutritionRecord, OvulationTestRecord, OxygenSaturationRecord, PowerRecord, RespiratoryRateRecord, RestingHeartRateRecord, SexualActivityRecord, SleepSessionRecord, SleepStageRecord, SpeedRecord, StepsCadenceRecord, StepsRecord, SwimmingStrokesRecord, TotalCaloriesBurnedRecord, Vo2MaxRecord, WaistCircumferenceRecord, WeightRecord, WheelchairPushesRecord
  12. ActiveCaloriesBurnedRecord, BasalBodyTemperatureRecord, BasalMetabolicRateRecord, BloodGlucoseRecord, BloodPressureRecord, BodyFatRecord, BodyTemperatureRecord, BoneMassRecord, CervicalMucusRecord, CyclingPedalingCadenceRecord,

    DistanceRecord, ElevationGainedRecord, ExerciseEventRecord, ExerciseLapRecord, ExerciseRepetitionsRecord, ExerciseSessionRecord, FloorsClimbedRecord, HeartRateRecord, HeightRecord, HipCircumferenceRecord, HydrationRecord, LeanBodyMassRecord, MenstruationFlowRecord, NutritionRecord, OvulationTestRecord, OxygenSaturationRecord, PowerRecord, RespiratoryRateRecord, RestingHeartRateRecord, SexualActivityRecord, SleepSessionRecord, SleepStageRecord, SpeedRecord, StepsCadenceRecord, StepsRecord, SwimmingStrokesRecord, TotalCaloriesBurnedRecord, Vo2MaxRecord, WaistCircumferenceRecord, WeightRecord, WheelchairPushesRecord II. HealthConnectͷσʔλछผΛܾΊΔ 2. HealthConnectͷσʔλछผʢRecordΠϯλʔϑΣʔεʣʹఆٛ͞Εͨ
 ৺ഥɺϚΠϯυϑϧωεɺา਺ɺਭ຾ɺΤωϧΪʔফඅྔͷΫϥεΛ୳͢… 35 ActiveCaloriesBurnedRecord HeartRateRecord SleepSessionRecord SleepStageRecord StepsRecord TotalCaloriesBurnedRecord
  13. II. HealthConnectͷσʔλछผΛܾΊΔ 36 3. UpmindΞϓϦͰѻ͏σʔλΛཧղͯ͠ɺHealthConnectͷσʔλछผΛ୳͢ ɾ৺ഥ → 1෼ؒͷ৺ഥ਺ ɾϚΠϯυϑϧωε →

    Ϣʔβ͕࣮ࢪͨ͠վળίϯςϯπʢᛉ૝ɺϤΨɺετϨονʣ ɾา਺ → 1೔ͷา਺ ɾਭ຾ → 1೔ͷਭ຾࣌ؒʢਭ຾ͷਂ͞͸আ͘ʣ ɾΤωϧΪʔফඅྔ → Ϣʔβ͕1೔׆ಈͯ͠ফඅͨ͠ΤωϧΪʔ
  14. II. HealthConnectͷσʔλछผΛܾΊΔ 37 ɾ৺ഥ → HeartRateRecord ɾϚΠϯυϑϧωε → ExerciseSessionRecord
 ɹɹɹɹɹɹɹɹɹɹ(

    ExerciseType.MEDITATION / YOGA / STRETCHING ) ɾา਺ → StepsRecord ɾਭ຾ → SleepSessionRecord, SleepStageRecord ɾΤωϧΪʔফඅྔ → ActiveCaloriesBurnedRecord, TotalCaloriesBurnedRecord
  15. II. HealthConnectͷσʔλछผΛܾΊΔ 38 Android / HealthConnect iOS / HealthKit ɹ৺ഥ਺

    HeartRateRecord HKQuantityTypeIdentifier.
 heartRate ɹ࣮ࢪͨ͠ 
 ɹվળίϯςϯπ ExerciseSessionRecord HKCategoryTypeIdentifier.
 mindfulSession ɹา਺ StepsRecord HKQuantityTypeIdentifier.
 stepCount ɹਭ຾ SleepSessionRecord HKCategoryTypeIdentifier.
 sleepAnalysis ɹফඅΧϩϦʔ ActiveCaloriesBurnedRecord HKQuantityTypeIdentifier.
 activeEnergyBurned
  16. II. HealthConnectͷσʔλछผΛܾΊΔ 4. ΞϓϦͰ࢖༻͢ΔݖݶΛ res/values/health_permissions.xml ʹఆٛ͢Δ 39 <resources> <!-- ΞϓϦ͔ΒΞΫηε͢ΔHealthConnectͷσʔλछผͱݖݶ

    --> <array name=“health_permissions”> <item>androidx.health.permission.HeartRate.WRITE</item> <item>androidx.health.permission.ActivitySession.WRITE</item> <item>androidx.health.permission.Steps.READ</item> <item>androidx.health.permission.SleepSession.READ</item> <item>androidx.health.permission.ActiveCaloriesBurned.READ</item> </array> </resources>
  17. IV. HealthConnectͷݖݶऔಘ 2. ݖݶΛཁٻ͢ΔIntentͷLauncherΛੜ੒͢Δ 41 val permissionContract: ActivityResultContract< Set<HealthPermission>, Set<HealthPermission>

    > = PermissionController.createRequestPermissionResultContract() // JetpackComposeͷ `@Composable` ϝιου಺ val launcher = rememberLauncherForActivityResult(permissionContract) { // TODO: ݖݶͷཁٻ݁Ռ `Set<HealthPermission>` Λݕূ͢Δ }
  18. IV. HealthConnectͷݖݶऔಘ 3. ݖݶΛཁٻ͢Δը໘ભҠΛ͓͜ͳ͏ 42 val PERMISSIONS = setOf( HealthPermission.createWritePermission(HeartRateRecord::class),

    HealthPermission.createWritePermission(ExerciseSessionRecord::class), HealthPermission.createReadPermission(StepsRecord::class), HealthPermission.createReadPermission(SleepSessionRecord::class), HealthPermission.createReadPermission(ActiveCaloriesBurnedRecord::class), ) launcher.launch(PERMISSIONS)
  19. IV. HealthConnectͷݖݶऔಘ 44 5. ཁٻ݁Ռ͔Βݖݶ͕ڐՄ͞Ε͔ͨݕূ͢Δ val client: HealthConnectClient = HealthConnectClient.getOrCreate(context)

    /** @return [PERMISSIONS]ͷݖݶ͢΂͕ͯڐՄ͞Ε͔ͨ */ override suspend fun isAllGranted(): Boolean { return PERMISSION == client.permissionController.getGrantedPermissions(PERMISSION) } /** @return [permissions]ͷݖݶ͕ڐՄ͞Ε͔ͨ */ override suspend fun isGranted(permission: Set<HealthPermission>): Boolean { return permission == client.permissionController.getGrantedPermissions(permission) }
  20. V. HealthConnectͷσʔλಡΈऔΓ 45 ಡΈऔΓϦΫΤετΛੜ੒ͯ͠ɺσʔλΛऔಘ͢Δ /** @return 1೔෼ʢ[date]ʣͷา਺ */ suspend fun

    readSteps(date: LocalDate): List<StepsRecord> { val start = date.atStartOfDay().truncatedTo(ChronoUnit.HOURS) val end = start.plusDays(1L).minusMinutes(1L) val timeRange = TimeRangeFilter.between(start, end) val request = ReadRecordsRequest( recordType = StepsRecord::class, timeRangeFilter = timeRange, ) return client.readRecords(request).records }
  21. VI. HealthConnectͷσʔλॻ͖ࠐΈ 46 ॻ͖ࠐΈϦΫΤετΛੜ੒ͯ͠ɺσʔλΛऔಘ͢Δ /** ࣮ࢪͨ͠ᛉ૝Λॻ͖ࠐΉ */ suspend fun writeMeditation(

    title: String, startTime: LocalDateTime, durationMinutes: Long ) { val zoneOffset = OffsetDateTime.now().offset val session = ExerciseSessionRecord( title = title, exerciseType = ExerciseSessionRecord.ExerciseType.MEDITATION, startTime = startTime.minusMinutes(durationMinutes).toInstant(zoneOffset), startZoneOffset = zoneOffset, endTime = startTime.toInstant(zoneOffset), endZoneOffset = zoneOffset, ) client.insertRecords(listOf(session)) }