[안드로이드] Health Connect 연동 기록

말랑돌·2024년 12월 31일

안드로이드 학습

목록 보기
2/17

헬스 커넥트(Health Connect)란?

삼성에서 제공하는 어플 삼성 헬스의 데이터를 동기화하여, 다른 어플에서 사용하기 위한 어플이다.

멤버쉽 결게를 하지 않는 이상, 삼성 헬스의 데이터를 직접 받는 것은 불가능 하다.

따라서 삼성 헬스의 데이터를 헬스 커넥트에 저장하고, 그 데이터를 사용하기 위한 접근을 해야한다.


코드 작성

build.gradle.kts (:App)

android {
    ...
    compileSdk = 35
	...
}

...

dependencies {
	...
	implementation("androidx.health.connect:connect-client:1.1.0-alpha10")
	...
}

targetSdk는 35로 하면 에러가 발생하는데, compileSdk는 35로 설정해야한다.

그래서 이거 만들 때 targetSdk=34다.

이 에러의 문제는 헬스 커넥트의 의존성 버전이 높아서 그런 것 같다.

만약 targetSdk를 줄일려면, 의존선 알파 버전도 좀 줄이는 것이 좋겠다.


AndroidManifest.xml

<manifest>
	<queries>
		<package android:name="com.google.android.apps.healthdata"/>
	</queries>
	<uses-permission android:name="android.permission.health.READ_STEPS"/>
	<uses-permission android:name="android.permission.health.WRITE_STEPS"/>
    ...
    <application>
    	<activity>
        	...
        	<!--   TODO: Add intent filter to handle permission rationale intent   -->
			<!--   Permission handling for Android 13 and before   -->
			<intent-filter>
				<action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE"/>
			</intent-filter>
			<!--   Permission handling for Android 14 and later   -->
			<intent-filter>
				<action android:name="android.intent.action.VIEW_PERMISSION_USAGE"/>
				<category android:name="android.intent.category.HEALTH_PERMISSIONS"/>
			</intent-filter>
			...
        </activity>
    </application>
</manifest>

uses-permission의 경우, 읽거나(read) 쓸 수 있는(write) 데이터의 종류가 매우 많다. 그에 따라서 추가할 수 있다.

activty 내부의 추가 내용은 권한(Permission) 확인 및 사용을 위해서 추가하는 것이다.

Android 13 이하와 Android 14 이상으로 구분되어, 그에 따라 권한 확인 방식이 다른데, 자세히 어떤 방식으로 다른지는 모른다.


MainActivity.kt

class MainActivity : ComponentActivity() {

    lateinit var requestPermissions : ActivityResultLauncher<Set<String>>
    lateinit var  healthConnectClient : HealthConnectClient
    
    //권한 목록
    val permissions = setOf(
        HealthPermission.getReadPermission(StepsRecord::class),
        HealthPermission.getWritePermission(StepsRecord::class)
    )

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

		//lifecycleSocpe란?
        lifecycleScope.launch {
            checkInstallHC()
        }


        setContent {
            HCTheme {
                // A surface container using the 'background' color from the theme
                Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
                    Greeting("Android")
                }
            }
        }
    }

    //헬스 커넥트 플레이스토어 이동
    private fun openPlayStoreForHealthConnect(){
        val intent = Intent(Intent.ACTION_VIEW).apply {
            data = Uri.parse("https://play.google.com/store/apps/details?id=com.google.android.apps.healthdata")
            setPackage("com.android.vending")
        }
        startActivity(intent)
    }

    //헬스커넥트 설치, 버전, 권한 여부 확인
    private fun checkInstallHC (){
        val providerPackageName = "com.google.android.apps.healthdata"
        val availabilityStatus = HealthConnectClient.getSdkStatus(this, providerPackageName)
        
        //헬스 커넥트 앱이 없을 때
        if (availabilityStatus == HealthConnectClient.SDK_UNAVAILABLE) {
            Toast.makeText(this, "헬스 커넥트 앱을 설치 또는 업데이트 해주세요", Toast.LENGTH_SHORT).show()
            finish()
            openPlayStoreForHealthConnect()
        }
        
        //헬스 커넥트 앱이 최신 버전이 아닐때
        if (availabilityStatus == HealthConnectClient.SDK_UNAVAILABLE_PROVIDER_UPDATE_REQUIRED) {
            Toast.makeText(this, "헬스 커넥트 앱을 설치 또는 업데이트 해주세요", Toast.LENGTH_SHORT).show()
            finish()
            openPlayStoreForHealthConnect()
        }

        healthConnectClient = HealthConnectClient.getOrCreate(this)

        // 권한(permission)이 부여되어 있는지 확인
        val requestPermissionActivityContract = PermissionController.createRequestPermissionResultContract()
        requestPermissions = registerForActivityResult(requestPermissionActivityContract) { granted ->
            //권한이 부여된 상태
            if (granted.containsAll(permissions)) {
                Log.d("PermissionO", "$healthConnectClient")
                lifecycleScope.launch {
                    readStepsByTimeRange()
                }
            }
            //권한이 부여되지 않은 상태 -> 권한 부여하러 이동
            else {
                Log.d("PermissionX", "$healthConnectClient")
                movePermissionSetting(this)
            }
        }

        requestPermissions.launch(permissions)
    }

    //권한 설정 화면으로 이동
    private fun movePermissionSetting(context: Context) {
        val packageName = "com.google.android.apps.healthdata"
        val intent = packageManager.getLaunchIntentForPackage(packageName)
        context.startActivity(intent)
        Toast.makeText(context, "앱 권한 → '걸음 수'를 활성화 해주세요.", Toast.LENGTH_LONG).show()
    }

    //하루동안의 걸음 수 불러오기
    private suspend fun readStepsByTimeRange() {
        val now: LocalDateTime = LocalDateTime.now()
        val startOfDay = LocalDateTime.of(now.toLocalDate(), LocalTime.MIDNIGHT)

        try {
            val response = healthConnectClient.readRecords(
                ReadRecordsRequest(
                    recordType = StepsRecord::class,
                    timeRangeFilter = TimeRangeFilter.between(startOfDay, now)
                )
            )


            Toast.makeText(this, response.records[0].count.toString(), Toast.LENGTH_SHORT).show()


			//테스트용 : 헬스 커넥트에 데이터 쓰기
            val startTime = Instant.now().minusSeconds(3600)
            val endTime = Instant.now()
            val stepsRecord = StepsRecord(startTime, ZoneOffset.UTC, endTime, ZoneOffset.UTC, 10000)
			healthConnectClient.insertRecords(listOf(stepsRecord))

        } catch (e: Exception) {
            Toast.makeText(this, "$e", Toast.LENGTH_SHORT).show()
        }
    }
}

@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
    Text(
        text = "Hello $name!",
        modifier = modifier
    )
}

의문점 : 처음에 lifecycleScope로 함수를 호출하는 이유는 모른다.
나는 비동기 함수만 저걸로 호출하는 줄 알았는데, 아니었나봄


결과

반성공. 반실패

👍성공이유

  • 첫 접속에는 데이터가 없어서 에러 발생 -> 다음 접속 부터 테스트용 write한 데이터가 있기 때문에 해당 데이터를 불러옴
  • 코드를 조금만 수정하면, 처음부터 데이터 불러오기가 가능할 듯

👎실패 이유

  • 삼성 헬스에서 헬스 커넥트로 데이터를 저장하는 행위는 따로 설정이 필요
  • 실시간 데이터 로딩 불가능
    • 최대 하루의 시간이 걸려서 삼성 헬스 -> 헬스 커넥트로 데이터 이동



아래 링크의 글을 보면 다음과 같다.

  1. 삼성 헬스 앱 > 상단 점 3개 더보기 > 설정 > 삼성 계정과 동기화 > 사용 중 ON > 지금 동기화 > 동기화
    **동기화가 정상적으로 완료되면, 마지막 동기화 시간이 현재시간으로 변경됩니다.
  2. 삼성 헬스 앱 > 상단 점 3개 더보기 > 설정 > 헬스 커넥트 > 앱 권한 > Samsung Health > 모두 허용 (읽기/쓰기 권한 모두 허용)
    *읽기/쓰기 방향과 관계 없이 Health Connect와 연동을 원하실 경우, 해당 데이터 항목에 대해 읽기/쓰기 양쪽 권한을 모두 부여해 주셔야 합니다.

연동이 목록에는 바로 보이지만 헬스커넥트를 통해서 다른 앱에서 걸음수를 가져가는건 시간이 걸리는것으로 확인하였습니다.
최대 1일까지 소요되는것 같구요 기기별 편차가 존재하는것 같습니다.

https://r1.community.samsung.com/t5/samsung-health/%EC%82%BC%EC%84%B1%ED%97%AC%EC%8A%A4%EC%99%80-%ED%97%AC%EC%8A%A4%EC%BB%A4%EB%84%A5%ED%8A%B8-%EC%97%B0%EA%B2%B0/td-p/23874271

0개의 댓글