Android Sleep API Codelab

빌드할 항목

이 Codelab에서는 사용자의 수면 시간을 감지하는 Android 앱을 빌드합니다. 앱은 다음과 같은 작업을 합니다.

  • 권한 요청
  • Android Sleepad API 등록
  • API 이벤트를 가져와 UI에 표시
  • 더 이상 필요하지 않은 경우 등록 취소

필요한 항목

  • Android 빌드 도구 v21 이상이 설치된 최신 버전의 Android 스튜디오
  • Android 10(API 수준 29) 이상을 실행하는 기기

시작 프로젝트 저장소 클론

Google에서 준비한 시작 프로젝트를 사용하면 신속하게 빌드할 수 있습니다. 터미널/명령줄에 git --version을 입력하여 git이 있는지 확인합니다. 설치되어 있지 않으면 이 안내에 따라 설치합니다. 그런 다음 아래 명령어를 실행하기만 하면 프로젝트를 클론할 수 있습니다.

 git clone https://github.com/googlecodelabs/android-sleep/

프로젝트 가져오기

Android 스튜디오를 시작하고 시작 화면에서 'Open an existing Android Studio project'를 선택하고 프로젝트 디렉터리를 엽니다.

프로젝트가 로드된 후 git이 일부 로컬 변경사항을 추적하지 않는다는 알림이 표시될 수도 있습니다. 오른쪽 상단에서 'Ignore' 또는 'X'를 클릭하면 됩니다. 변경사항이 Git 저장소로 다시 푸시되지 않습니다.

Android 뷰에서는 프로젝트 창의 왼쪽 상단에 다음과 같은 이미지가 표시됩니다. Project 뷰에서는 동일한 내용을 보려면 프로젝트를 확장해야 합니다.

1401a11c10711a60.png

폴더 아이콘 두 개(startcomplete)가 있습니다. 이들을 각각 '모듈'이라고 합니다.

Android 스튜디오에서 처음으로 프로젝트를 백그라운드에서 컴파일할 때는 몇 초 정도 걸릴 수 있습니다. 그동안 Android 스튜디오 하단의 상태 표시줄에 스피너가 표시됩니다.

4bc64eb3b99eb0ae.png

이 작업이 끝날 때까지 기다린 다음에 코드를 변경하는 것이 좋습니다. 그러면 Android 스튜디오에서 필요한 모든 구성요소를 가져올 수 있습니다.

그 외에 'Reload for language changes to take effect?'라는 메시지나 이와 유사한 메시지가 표시되면 'Yes'를 선택합니다.

시작 프로젝트 이해

좋습니다. 이제 설정이 다 되었으며 활동 감지를 추가할 준비가 되었습니다. 이 Codelab의 시작 지점인 start 모듈을 사용하겠습니다. 다시 말해서 각 단계의 코드를 start에 추가해 보겠습니다.

complete 모듈은 작업을 확인하거나 문제가 발생했을 때 참고하는 용도로 사용할 수 있습니다.

주요 구성요소 개요:

  • MainActivity.kt: 사용자가 앱을 시작할 때 앱의 기본 화면을 렌더링합니다.
  • SleepReceiver.kt: API에서 sleep 이벤트를 추출하여 데이터베이스에 저장합니다.
  • BootReceiver.kt: Sleep API 이벤트를 계속 수신 대기하기 위해 기기 부팅이 완료되면 Sleep API에 다시 등록합니다.

시작 프로젝트 실행

앱을 실행해 보겠습니다.

  1. Android 기기를 컴퓨터에 연결합니다.
  2. 툴바의 드롭다운 선택기에서 start 구성을 선택하고 기기를 선택한 다음 기기 옆에 있는 녹색 삼각형(실행) 버튼을 클릭합니다.

177045a302bf57d8.png

기기에 애플리케이션이 표시됩니다.

30efe28b9757e1e7.png

311c83ca64556d94.png

실제로는 아직 수면 추적 코드를 추가하지 않았습니다. 이 코드는 다음 섹션에서 나옵니다.

다음 섹션에서는 필요한 라이브러리와 권한을 검토해 보겠습니다.

build.gradle 및 AndroidManifest.xml 검토

앱에서 Sleep API를 사용하려면 Google Location and Activity Recognition API의 종속 항목을 선언하고 앱 매니페스트에서 com.google.android.gms.permission.ACTIVITY_RECOGNITION 권한을 지정해야 합니다.

  1. start 모듈의 build.gradle 파일에서 TODO: Review play services library required for activity recognition을 검색합니다. 이 단계에서 수행할 작업은 없습니다. 선언된 필수 종속 항목을 검토하기만 하면 됩니다. 다음과 같이 나타납니다.
    // TODO: Review play services library required for activity recognition.
    implementation 'com.google.android.gms:play-services-location:18.0.0'
  1. start 모듈의 AndroidManifest.xml에서 TODO: Add activity recognition and boot complete permissions를 검색하고 다음 코드를 <manifest> 요소에 추가합니다.
<!-- TODO: Add activity recognition and receive boot complete permissions. -->
<!-- Required for 29+. -->
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

이제 코드가 다음과 비슷하게 표시됩니다.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.example.sleepcodelab">
...
<!-- TODO: Add activity recognition and receive boot complete permissions. -->
<!-- Required for 29+. -->
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
  ...
</manifest>

주석에서 볼 수 있듯이 API 버전 29의 런타임 권한을 추가해야 합니다.

이제 완료됐습니다. 이제 앱에서 수면 활동 감지를 지원할 수 있습니다. 수면 활동 감지를 얻기 위한 코드를 추가하기만 하면 됩니다.

활동 감지 권한 확인 검토

필요한 경우 런타임 권한을 확인하고 요청해야 합니다.

  • MainActivity.kt에서 활동 감지 권한을 확인합니다.
  • 권한이 부여되지 않았으면 displayPermissionSettingsSnackBar()를 호출합니다. 이 스낵바는 권한이 필요한지 설명해 주고, 사용자가 시스템 설정에서 권한을 승인할 수 있게 해 줍니다.

start 모듈의 MainActivity.kt에서 TODO: Review Activity Recognition permission check를 검색합니다. 다음 코드 스니펫이 표시됩니다.

Note, there is no action for this section.

// TODO: Review Activity Recognition permission checking.
private fun activityRecognitionPermissionApproved(): Boolean {
   return PackageManager.PERMISSION_GRANTED == ActivityCompat.checkSelfPermission(
           this,
           Manifest.permission.ACTIVITY_RECOGNITION
   );
}

수면 추적 사용/중지

start 모듈의 MainActivity.kt에서 TODO: Enable/Disable Sleep tracking and ask for permissions if needed를 검색합니다. onClickRequestSleepData() 메서드를 다음 코드로 바꿉니다.

// TODO: Enable/Disable sleep tracking and ask for permissions if needed.
fun onClickRequestSleepData(view: View) {
   if (activityRecognitionPermissionApproved()) {
       if (subscribedToSleepData) {
           unsubscribeToSleepSegmentUpdates(applicationContext, sleepPendingIntent)
       } else {
           subscribeToSleepSegmentUpdates(applicationContext, sleepPendingIntent)
       }
   } else {
       requestPermissionLauncher.launch(permission.ACTIVITY_RECOGNITION)
   }
}

활동 감지 권한이 승인되고 사용자가 수면 데이터를 구독한 경우 수면 업데이트를 구독합니다. 그 외에는 구독을 취소합니다.

권한이 승인되지 않은 경우, 사용자는 권한이 필요한 이유가 설명되어 있으며 권한을 부여할 수 있는 스플래시 화면 활동으로 이동합니다.

PendingIntent 만들기

start 모듈의 MainActivity.kt에서 TODO: Create a PendingIntent for Sleep API events를 검색합니다. 다음 스니펫을 붙여넣습니다.

// TODO: Create a PendingIntent for Sleep API events
sleepPendingIntent =
   SleepReceiver.createSleepReceiverPendingIntent(context = applicationContext)

이제 Sleep API 데이터를 사용할 수 있을 때 업데이트를 받을 수 있습니다.

Sleep API 업데이트 요청

start 모듈의 MainActivity.kt에서 TODO: Request Sleep API updates를 검색합니다. 다음 스니펫을 붙여넣습니다.

// TODO: Request Sleep API updates
val task = ActivityRecognition.getClient(context).requestSleepSegmentUpdates(
   pendingIntent,
   // Registers for both SleepSegmentEvent and SleepClassifyEvent data.
   SleepSegmentRequest.getDefaultSleepSegmentRequest()
)

task.addOnSuccessListener {
   mainViewModel.updateSubscribedToSleepData(true)
   Log.d(TAG, "Successfully subscribed to sleep data.")
}
task.addOnFailureListener { exception ->
   Log.d(TAG, "Exception when subscribing to sleep data: $exception")
}

Sleep API 업데이트를 성공적으로 등록하면 앱은 등록된 PendingIntent에서 수면 감지 알림을 받습니다.

부팅 완료 시 다시 구독

start 모듈의 receiver/BootReceiver.kt에서 TODO: Request Sleep API upon boot complete를 검색합니다. 다음 스니펫을 붙여넣습니다.

// TODO: Request Sleep API upon boot complete
val subscribedToSleepData = repository.subscribedToSleepDataFlow.first()
if (subscribedToSleepData) {
   subscribeToSleepSegmentUpdates(
       context = context,
       pendingIntent = SleepReceiver.createSleepReceiverPendingIntent(context)
   )
}

이 코드를 사용하면 기기가 재부팅된 후 앱에서 Sleep API 업데이트를 계속 받을 수 있습니다.

sleep classification 또는 sleep time detection 이벤트가 발생하면 앱은 Intent 콜백을 수신합니다. SleepSegmentEvent 또는 SleepClassifyEvent 객체의 목록을 인텐트에서 추출할 수 있습니다.

이러한 이벤트를 처리하기 위해 코드를 추가해 보겠습니다.

start 모듈의 receiver/SleepReceiver.kt에서 TODO: Extract sleep information from PendingIntent를 검색합니다. 주석 뒤에 다음 코드를 추가합니다.

// TODO: Extract sleep information from PendingIntent.
if (SleepSegmentEvent.hasEvents(intent)) {
   val sleepSegmentEvents: List<SleepSegmentEvent> =
       SleepSegmentEvent.extractEvents(intent)
   addSleepSegmentEventsToDatabase(repository, sleepSegmentEvents)
} else if (SleepClassifyEvent.hasEvents(intent)) {
   val sleepClassifyEvents: List<SleepClassifyEvent> =
       SleepClassifyEvent.extractEvents(intent)
   addSleepClassifyEventsToDatabase(repository, sleepClassifyEvents)
}

그러면 SleepSegmentEvent 또는 SleepClassifyEvent 데이터가 Room 데이터베이스에 저장됩니다. MainActivity가 LiveData를 사용하여 ViewModel을 통해 데이터베이스 정보에 액세스합니다. 이러한 클래스의 호환 방법을 자세히 알아보려면 뷰를 사용한 Room Codelab을 확인하세요.

모두 끝났습니다. 앱을 실행해 봅니다.

케이블을 사용하여 기기와 워크스테이션을 연결합니다. Android 스튜디오에서 'Run'을 클릭하여 기기에 앱을 설치하고 실행합니다. 활동 감지 권한을 부여하고 SUBSCRIBE TO SLEEP DATA 버튼을 탭하면 첫 번째 SleepClassifyEvent가 약 10분 뒤에 표시됩니다. SleepSegmentEvent는 하루 내에 표시됩니다.

원하는 경우 코드를 전체적으로 꼼꼼히 살펴보고 지금까지 한 작업을 검토해 코드가 함께 어떻게 작동하는지 더 확실히 파악해 보세요.

코드 검토(선택사항)

다음 코드 검토는 선택사항입니다. Sleep API와 데이터베이스 항목 간의 세부 데이터 매핑 정보를 제공하는 코드입니다.

  • data/db/SleepClassifyEventEntity.kt
  • data/db/SleepSegmentEventEntity.kt

참조 문서