google play api는 google play service의 하나의 파트 임.
google fit api는 안드로이드 4.1 이상부터 호환.
Google Fit API 장점
- 거의 실시간의 히스토리 데이터를 적은 에너지의 블루투스 디바이스로부터 가져옴
- 활동들을 기록 할 수 있음
- 데이터를 세션과 연관시킬 수 있음
- 피트니스 목표를 설정 할 수 있음
sensor data
- 유저의 하루 활동 관련한 정보들을 앱에서 제공한다면 (예를 들어 하루동안의 걸음수), sensor data는 사용자의 행동을 거의 실시간으로 보여주는데 사용이 가능함.
record data
- 앱이 꺼져있는 상황에서도 계속해서 데이터를 적재 할 수 있는 subscribe 메서드를 제공함
historical data
- 만약 유저가 과거의 활동들로부터 피트니스 데이터를 보여주기를 원한다면 history api를 사용하면 됨
- 데이터 subscribe가 선행 되어야 historical data를 읽을 수 있음 ( 도큐먼트가 친절하지 않아서 테스트 후에 알게 됨), 데이터가 없으면 빈 리스트로 응답이 옴.
session data
- 개발자가 일정한 시간의 세션을 설정하고 그 설정안에 들어가는 범위의 데이터를 가져올 수 있음
내가 하고자 하는 것
- subscribe를 통해 앱 사용자의 활동내역(현재는 걸음 수, 이후에 추가할 예정)들을 클라우드 형태의 google fit store에 앱을 종료했을 때도 마찬가지로 계속해서 저장해야 함.
- 데이터가 있는 날부터 일(00시~24시) 기준으로 걸음 수 데이터를 보여줄 수 있어야 함
- 배터리 최적화, 잠자기 모드 등 예기치 못한 상황에서도 정상적으로 데이터를 가져와야 함
- 데이터는 자정 기준으로 새로 시작함
사전 준비
- google fit api 는 Google API Console 에 프로젝트를 등록 및 client ID 를 발급 받은 후 사용해야함.
- client ID 발급 후 사용할 library에서 google fit api 를 사용 설정 해야 함.
- 테스트를 위해서 OAuth 동의 화면 탭의 테스트 사용자에 테스트를 수행 할 사용자 정보를 등록해야 함.
- 모듈 범위의 gradle 에
plugin {
id("com.android.application")
}
...
dependencies {
implementation("com.google.android.gms:play-services-fitness:21.1.0")
implementation("com.google.android.gms:play-services-auth:20.2.0")
}
다음 과 같이 dependency를 추가하여 필요한 라이브러리를 다운로드 함
- https://developers.google.com/fit/android/get-started 를 참고하면 더 자세히 setup에 관해 나와 있음
구현 방법
- fitness option 객체를 만들어 내가 사용하고자 하는 data들을 추가함
funcreateFitnessStepOptions() : FitnessOptions{
val fitnessOptions = FitnessOptions.builder()
.addDataType(DataType.TYPE_STEP_COUNT_DELTA, FitnessOptions.ACCESS_READ)
.addDataType(DataType.AGGREGATE_STEP_COUNT_DELTA, FitnessOptions.ACCESS_READ)
.addDataType(DataType.TYPE_DISTANCE_DELTA, FitnessOptions.ACCESS_READ)
.addDataType(DataType.AGGREGATE_DISTANCE_DELTA, FitnessOptions.ACCESS_READ)
.build()
return fitnessOptions
}
걸음수와 지금까지 걸은 거리에 대한 데이터를 얻기 위해 위처럼 4개의 fitness data type을 추가함.
- google account를 가져옴
fun getAccount(fitnessOptions: FitnessOptions) : GoogleSignInAccount{
val account = GoogleSignIn.getAccountForExtension(GlobalApplication.getApplicationContext(),fitnessOptions)
return account
}
getAccountForExtension 메서드를 이용하여 추가한 데이터에 대한 인증을 사용하는 google signin account를 얻음.
- google fit api를 사용하기 위한 권한 요청
fun checkHasGrantedDataAccess(account: GoogleSignInAccount, fitnessOptions: FitnessOptions, callback: () -> Unit){
Log.i(TAG,"checkHasGrantedDataAccess${account.id}, fitnessOptions${fitnessOptions}")
if(!GoogleSignIn.hasPermissions(account, fitnessOptions)){
//해당 google id가 permission이 허용되었는지 체크함.
Log.d(TAG,"!GoogleSignIn.hasPermissions")
GoogleSignIn.requestPermissions(mActivity,GOOGLE_FIT_PERMISSIONS_REQUEST_CODE, account, fitnessOptions)
}else{
callback()
}
}
account 가 접근하고자 하는 google fit api data에 대한 권한이 있는지 체크하고, 권한이 있다면 callback함수를 실행함. 없다면 권한을 요청함.
- 걸음 수 / 걸은 거리 데이터 구독 시작
//피트니스 데이터(걸음 수)구독셋팅
fun subscribeStepCountData(successCallback: () -> Unit?, failureCallback: () -> Unit?){
Fitness.getRecordingClient(GlobalApplication.getApplicationContext(),mGoogleSignInAccount)
.subscribe(DataType.TYPE_STEP_COUNT_DELTA)// no scopes are specified.
.addOnSuccessListener{
Log.i(TAG,"successfully subscribe step count data!!")
successCallback()
}.addOnFailureListener{
Log.w(TAG,"there was a problem subscribing step count data${it}")
failureCallback()
}
}
- 걸음 수를 원하는 날짜부터 가져옴 (수정 중)
fun getStepCountForDays(days : Int){
val cal = Calendar.getInstance()
val now = Date()
cal.time= now
val endTime = cal.timeInMillis
cal.set(Calendar.HOUR_OF_DAY, -days)
cal.set(Calendar.MINUTE, 0)
cal.set(Calendar.SECOND, 0)
cal.set(Calendar.MILLISECOND, 0)
val startTime = cal.timeInMillis
val readRequest = DataReadRequest.Builder()
.aggregate(DataType.TYPE_STEP_COUNT_DELTA, DataType.AGGREGATE_STEP_COUNT_DELTA)
.bucketByTime(1, TimeUnit.DAYS)
.setTimeRange(startTime, endTime, TimeUnit.SECONDS)
.build()
Fitness.getHistoryClient(mActivity,mGoogleSignInAccount)
.readData(readRequest)
.addOnSuccessListener{
response->
for(dataSetinresponse.buckets.flatMap{ it.dataSets}) {
Log.i(TAG,"dataSet ==${dataSet}")
//원하는 로직 추가 }
}.addOnFailureListener{
//실패 처리로직 추가
}
}
원하는 날부터 현재까지 일 단위의 걸음 수 데이터를 가져오는 로직
결과
테스트를 해보려고 구글 피트니스 앱과 데이터를 비교한 결과 데이터가 잘 조회 되는 것을 확인하였음. 이 과정에서 한번 더 고민하고 액션을 취했던 부분은 혹시나 하는 마음에 매일 자정이 되기 찰나의 순간에 하루동안의 데이터를 조회해 오는 메서드 readDailyTotal 를 호출하여 데이터를 저장하는 방법과, DataReadRequest 객체에 startTime과 endTime을 설정해 그 기간동안의 data를 가져와 사용하는 방법 간 데이터 결과에 차이가 있는가에 대한 테스트를 했고 데이터간 차이가 없는 것을 확인하고 후자의 방식으로 채택했음. 그러나 startTime과 endTime을 잘못 설정하면 자정이 기준이 아니고 현재 시간을 기준으로 하루가 책정되기 때문에 startTime과 endTime을 설정을 자신이 필요한 데이터에 따라 정확히 할 필요가 있음.