Get Started

๐Ÿšง

์ˆ˜๋ฉด ์ธก์ • ์‹œ ์•ˆ๋‚ด ์‚ฌํ•ญ์„ ๋”ฐ๋ผ์ฃผ์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

AsleepTrack์˜ ์ˆ˜๋ฉด ์ธก์ •/๋ถ„์„ ๊ธฐ๋Šฅ์„ ์ •ํ™•ํ•˜๊ฒŒ ํ…Œ์ŠคํŠธํ•˜๋ ค๋ฉด, โ€จํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ ๊ฐ€์ด๋“œ๋ฅผ ๋ฐ˜๋“œ์‹œ ๋”ฐ๋ผ์ฃผ์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค. ์ด ๊ฐ€์ด๋“œ๋ฅผ ์ค€์ˆ˜ํ•˜์ง€ ์•Š์€ ํ™˜๊ฒฝ์—์„œ ์–ป์€ ์ˆ˜๋ฉด ๋ถ„์„ ๊ฒฐ๊ณผ๋Š” โ€จ์‹ค์ œ ์ˆ˜๋ฉด ํŒจํ„ด๊ณผ ์ •ํ™•ํ•˜๊ฒŒ ์ผ์น˜ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Œ์„ ์œ ์˜ํ•ด์ฃผ์„ธ์š”.

  • ์ˆ˜๋ฉด ์ธก์ •์€ ์ตœ์†Œ 20๋ถ„ ์ด์ƒ์˜ ์˜ค๋””์˜ค๊ฐ€ ์—…๋กœ๋“œ ๋˜์–ด์•ผ ๋ถ„์„์ด ์‹œ์ž‘๋˜๋ฉฐ, ๊ทธ ์ดํ›„๋กœ๋Š” ํ”Œ๋žœ(๊ณ„์•ฝ ์กฐ๊ฑด)์— ๋”ฐ๋ผ 5๋ถ„ ๋˜๋Š” 20๋ถ„ ๊ฐ„๊ฒฉ์œผ๋กœ ๋ถ„์„ํ•ฉ๋‹ˆ๋‹ค.
    ๐Ÿ”— Test Environment Guideline ํ™•์ธํ•˜๊ธฐ
  • AsleepTrack์˜ ์ˆ˜๋ฉด ์ธก์ •/๋ถ„์„ ๊ธฐ๋Šฅ์„ ํ™•์ธํ•ด ๋ณผ ์ˆ˜ ์žˆ๋Š” ๋Œ€ํ•œ ์ƒ˜ํ”Œ ์ฝ”๋“œ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
    ๐Ÿ”— Sample App ํ™•์ธํ•˜๊ธฐ
  • Android SDK๋ฅผ ์ด์šฉํ•œ ์ˆ˜๋ฉด ๋ชจ๋‹ˆํ„ฐ๋ง ์•ฑ์„ ์‚ฌ์šฉํ•  ๋•Œ ๋‹ค๋ฅธ ๋…น์Œ/๋…นํ™” ์•ฑ๊ณผ ๊ฐ™์ด ์‹คํ–‰ ์ค‘์ด๋ฉด ์ˆ˜๋ฉด ์ธก์ •์ด ์ •์ƒ์ ์œผ๋กœ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
    • OS version 9 ์ดํ•˜ ์—์„œ๋Š” ์˜ค์ง ํ•œ ๊ฐœ์˜ ์•ฑ์—์„œ๋งŒ Audio ๋…น์Œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋ฉฐ, ๋จผ์ € ์‹คํ–‰๋œ ์•ฑ์—์„œ๋งŒ ๋…์ ์ด ๊ฐ€๋Šฅํ•˜๊ณ  ๋‹ค๋ฅธ ์•ฑ์—์„œ๋Š” Audio ๋…น์Œ ์‹คํ–‰์‹œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
    • OS version 10 ์ด์ƒ์—์„œ๋Š” ๋‘ ๊ฐœ ์ด์ƒ์˜ Audio ๋…น์Œ์•ฑ ์‹คํ–‰์‹œ ๊ฐ€์žฅ ๋‚˜์ค‘์— ์‹คํ–‰๋œ Audio ๊ด€๋ จ๋œ ์•ฑ์—์„œ ์ •์ƒ์ ์œผ๋กœ ๋…น์Œ์ด ๋˜๊ณ  ์ด์ „์— ์‹คํ–‰๋œ ์•ฑ๋“ค์€ Silence ์ฒ˜๋ฆฌ๋˜์–ด ์ˆ˜๋ฉด ๋ชจ๋‹ˆํ„ฐ๋ง ๋ถ„์„์‹œ ์ •์ƒ์ ์ธ ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.
      Android Developer Site

๐Ÿ“˜

์ธ์•ฑ ์—…๋ฐ์ดํŠธ

์•ฑ ์‚ฌ์šฉ์ž ์„ค์ •์— ๋”ฐ๋ผ ์•ฑ ์ž๋™ ์—…๋ฐ์ดํŠธ๊ฐ€ ์ด๋ค„์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ˆ˜๋ฉด์ธก์ • ์‹œ์ž‘ ํ›„ ์•ฑ ์—…๋ฐ์ดํŠธ๊ฐ€ ์ž๋™์œผ๋กœ ์ด๋ค„์ง€๊ฒŒ ๋˜๋ฉด ์˜๋„์น˜ ์•Š์€ ์•ฑ ์ข…๋ฃŒ๋กœ ์ˆ˜๋ฉด ์ธก์ •์ค‘ ๋น„์ •์ƒ ์ข…๋ฃŒ๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
์ด๋Ÿฌํ•œ ์ƒํ™ฉ์„ ๋ฏธ์—ฐ์— ๋ฐฉ์ง€ํ•˜๊ณ ์ž ๋ฏธ๋ฆฌ ์—…๋ฐ์ดํŠธ๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ์ธ์•ฑ ์—…๋ฐ์ดํŠธ๋ผ๋Š” ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด ์˜๋„์น˜ ์•Š์€ ์ƒํ™ฉ์„ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ”— ์ธ์•ฑ ์—…๋ฐ์ดํŠธ ๊ฐ€์ด๋“œ


1. Requirements

1.1 Minimum requirements on AsleepTrack SDK for Android

๐Ÿšง

  • Android 8.0 (API level 26) ํ˜น์€ ๋” ๋†’์€ ๋ฒ„์ „
  • Java 1.8 ํ˜น์€ ๋” ๋†’์€ ๋ฒ„์ „
  • Android Gradle plugin 8.0 ํ˜น์€ ๋” ๋†’์€ ๋ฒ„์ „

1.2 API Key

  • AsleepTrack SDK๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” API key๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
  • API key๋ฅผ ๋ฐœ๊ธ‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ๋Š” ์ด ๋งํฌ Generate API key๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”.

2. Getting Ready

2.1 Install AsleepTrack SDK and Settings

  1. Android Studio๋ฅผ ์ด์šฉํ•˜์—ฌ ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  2. AndroidManifest.xml ํŒŒ์ผ์„ ์—ด์–ด ํผ๋ฏธ์…˜์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE"/>
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
		...
    

๐Ÿ“˜

FOREGROUND_SERVICE ๊ถŒํ•œ์€ ์•ฑ ๊ฐœ๋ฐœ์‹œ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๊ฐœ๋ฐœํ•  ์•ฑ์ด ๋ฐค ์ค‘์— ๋Š๊น€์—†์ด ์ˆ˜๋ฉด์„ ์ธก์ •ํ•˜๊ฒŒ ํ•˜๋ ค๋ฉด FOREGROUND_SERVICE๋กœ SDK๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

https://developer.android.com/guide/components/foreground-services

์•ฑ์—์„œ Asleep SDK๋ฅผ ์ ์šฉํ•  ๋•Œ, Google ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(gms, chrome)์˜ ์—…๋ฐ์ดํŠธ๋กœ ์ธํ•ด ์ˆ˜๋ฉด ์ธก์ • ์ค‘ ์˜๋„์น˜ ์•Š๊ฒŒ ์•ฑ์ด ๊ฐ•์ œ๋กœ ์ข…๋ฃŒ๋˜๋Š” ํ˜„์ƒ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด foreground service์˜ process๋ฅผ ๋ถ„๋ฆฌํ•˜๋Š” ์ž‘์—…์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์ด์— ๋Œ€ํ•œ FGS ํ”„๋กœ์„ธ์Šค๊ฐ€ ๋ถ„๋ฆฌ๋œ ์ƒ˜ํ”Œ ์•ฑ์˜ ์ฝ”๋“œ๋ฅผ ์ฐธ๊ณ ํ•˜์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

๐Ÿ“˜

REQUEST_IGNORE_BATTERY_OPTIMIZATIONS ๊ถŒํ•œ์€ ์•ˆ์ •์ ์ธ ์ˆ˜๋ฉด์ธก์ •์„ ์œ„ํ•ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๋„์ฆˆ ๋ชจ๋“œ์— ๋น ์ง€๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ํ•ด๋‹น ๊ถŒํ•œ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

https://developer.android.com/training/monitoring-device-state/doze-standby

  1. ์•ฑ ์ˆ˜์ค€์˜ build.gradle ํŒŒ์ผ์„ ์—ด์–ด okhttp, gson ๋ฐ asleepsdk๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
plugins {
  ...
}

android {
	...
}

dependencies {
		...
    implementation 'com.squareup.okhttp3:okhttp:4.11.0'
    implementation 'com.google.code.gson:gson:2.10'
    implementation 'ai.asleep:asleepsdk:2.4.3'
}
  1. ๋งˆ์ดํฌ ๋…น์Œ ๊ธฐ๋Šฅ๊ณผ FGS์˜ notification์„ ์œ„ํ•œ ๊ถŒํ•œ ์†Œ์Šค์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.
ActivityCompat.requestPermissions(requireActivity(), 
                                  arrayOf(android.Manifest.permission.RECORD_AUDIO, 
                                          android.Manifest.permission.POST_NOTIFICATIONS), 
                                  0)

3. Sleep Tracking with AsleepTrack SDK

3.1 Step 1: Initialize the AsleepTrack SDK

  • 1.2์—์„œ ๋ฐœ๊ธ‰๋ฐ›์€ apiKey๋ฅผ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.
  • userId๊ฐ€ null์ด๋ฉด ์ƒˆ๋กœ์šด userId๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.
  • baseUrl์—๋Š” ํ”„๋ก์‹œ ์„œ๋ฒ„์˜ url ์ž…๋ ฅํ•˜์„ธ์š”. null์ด๋ฉด default base url์ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • callbackUrl์€ ๋ถ„์„ ๊ฒฐ๊ณผ๋ฅผ ์ง์ ‘ ๋ฐ›๋Š” ์„œ๋ฒ„์˜ url์„ ์ž…๋ ฅํ•˜์„ธ์š”. null์ด๋ฉด ์ฝœ๋ฐฑ์ด ์—†์Šต๋‹ˆ๋‹ค.
  • service๋Š” ๊ฐœ๋ฐœํ•  ์•ฑ์˜ ๋‹‰๋„ค์ž„์ด ์žˆ๋‹ค๋ฉด ์ž…๋ ฅํ•˜์„ธ์š”.
import ai.asleep.asleepsdk.Asleep
import ai.asleep.asleepsdk.data.AsleepConfig
import ai.asleep.asleepsdk.AsleepErrorCode

...

Asleep.initAsleepConfig(  
    context = applicationContext,  
    apiKey = "[input your apiKey]",  
    userId = null,  
    baseUrl = null,  
    callbackUrl = null,  
    service = "[input your AppName]",  
    object : Asleep.AsleepConfigListener {  
        override fun onSuccess(userId: String?, asleepConfig: AsleepConfig?) {  
						...
            /* save userId and asleepConfig */
  
        }
    override fun onFail(errorCode: Int, detail: String) {
        ...
    }
})

3.2 Step 2: Create SleepTrackingManager

  • initAsleepConfig์—์„œ ๋ฐ›์•„์˜จ AsleepConfig๋ฅผ ํŒŒ๋ผ๋ฉ”ํ„ฐ๋กœ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.
import ai.asleep.asleepsdk.tracking.SleepTrackingManager

...

var sleepTrackingManager = Asleep.createSleepTrackingManager(asleepConfig, object : SleepTrackingManager.TrackingListener {                           
    override fun onCreate() {
    }

    override fun onUpload(sequence: Int) {
    }
    
    override fun onClose(sessionId: String) {
        ...
        /* save sessionId */
    }
    
    override fun onFail(errorCode: Int, detail: String) {
    }
})

3.3 Step 3: Start Tracking

  1. ์ธก์ • ์‹œ์ž‘ํ•˜๊ธฐ
  • ์‹œ์ž‘์„ ํ•˜๋ฉด SleepTrackingManager ์ƒ์„ฑ ์‹œ ๋“ฑ๋กํ•œ ๋ฆฌ์Šค๋„ˆ์˜ onUpload ํ•จ์ˆ˜์—์„œ 30์ดˆ๋งˆ๋‹ค ์‹œํ€€์Šค ๊ฐ’์ด ์ฝœ๋ฐฑ๋ฉ๋‹ˆ๋‹ค.
sleepTrackingManager?.startSleepTracking()
  1. ์ธก์ • ์ค‘ ๋ถ„์„ํ•˜๊ธฐ
  • session์— ๋Œ€ํ•œ ์ •๋ณด๋Š” session data type ์ฐธ์กฐํ•˜์„ธ์š”.
sleepTrackingManager?.requestAnalysis(object : SleepTrackingManager.AnalysisListener {  
    override fun onSuccess(session: Session) {  
        Log.d("", "${session.toString()}")  
    }
}

3.4 Step 4: Stop Tracking

  • ์ •์ง€๋ฅผ ํ•˜๋ฉด SleepTrackingManager ์ƒ์„ฑ ์‹œ ๋“ฑ๋กํ•œ ๋ฆฌ์Šค๋„ˆ์˜ onCloseํ•จ์ˆ˜์—์„œ sessionId๊ฐ€ ์ฝœ๋ฐฑ๋ฉ๋‹ˆ๋‹ค.
sleepTrackingManager?.stopSleepTracking()

3.5 Step 5: Create Reports

  • initAsleepConfig์—์„œ ๋ฐ›์•„์˜จ AsleepConfig๋ฅผ ํŒŒ๋ผ๋ฉ”ํ„ฐ๋กœ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.
val reports = Asleep.createReports(asleepConfig)

3.6 Step 6: Get Report

  1. sessionId ํ•˜๋‚˜์˜ ๊ฒฐ๊ณผ ๋ฐ›์•„์˜ค๊ธฐ
  • sleepTracking์„ ์ •์ง€ํ–ˆ์„ ๋•Œ ๋ฐ›์•„์˜จ sessionId๋ฅผ ํŒŒ๋ผ๋ฉ”ํ„ฐ๋กœ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.
reports?.getReport(sessionId, object : Reports.ReportListener {
    override fun onSuccess(report: Report?) {
    }

    override fun onFail(errorCode: Int, detail: String) {
    }
})
  1. ๋‚ ์งœ๋กœ ๊ฒฐ๊ณผ ๋ฐ›์•„์˜ค๊ธฐ
val today = LocalDate.now()
reports?.getReports(today.minusDays(7).toString(), today.toString(), "DESC", 0, 20, object : Reports.ReportsListener {
    override fun onSuccess(reports: List<SleepSession>?) {
    }

    override fun onFail(errorCode: Int, detail: String) {
    }
})

3.7 Step 7: Remove Session

  1. ์ธก์ • ๊ธฐ๋ก ์‚ญ์ œํ•˜๊ธฐ
  • sessionId๋ฅผ ์ž…๋ ฅํ•˜์—ฌ ์ธก์ • ๊ธฐ๋ก์„ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค.
reports?.deleteReport(sessionId, object : Reports.DeleteReportListener {
    override fun onSuccess(sessionId: String?) {
    }

    override fun onFail(errorCode: Int, detail: String) {
    }
})
  1. ๋‚˜์˜ ์ •๋ณด ์ „๋ถ€ ์‚ญ์ œํ•˜๊ธฐ
  • userId์™€ ์ธก์ •๋œ ๊ธฐ๋ก์„ ์ „๋ถ€ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค. ์‚ญ์ œ๋œ userId๋Š” ๋” ์ด์ƒ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
Asleep.deleteUser(object : Asleep.DeleteUserIdListener {
    override fun onSuccess(userId: String?) {
    }

    override fun onFail(errorCode: Int, detail: String) {
    }
})

3.8 Step 8: Continue Session

  • v2.3.0 ์ด์ƒ๋ถ€ํ„ฐ ์‚ฌ์šฉ ๊ฐ€๋Šฅ

์•ˆ๋“œ๋กœ์ด๋“œ OS์—์„œ๋Š” Google Play ์„œ๋น„์Šค ํŒจํ‚ค์ง€๊ฐ€ ์—…๋ฐ์ดํŠธ๋  ๋•Œ๊ฐ€ ์žˆ๋Š”๋ฐ, ์ด๋•Œ ๋™์ž‘ ์ค‘์ธ ์•ฑ์˜ foreground service๊ฐ€ ์‹œ์Šคํ…œ์— ์˜ํ•ด ๊ฐ•์ œ๋กœ ์ข…๋ฃŒ ํ›„ ์žฌ์‹œ์ž‘ ๋ฉ๋‹ˆ๋‹ค. ์ด๋•Œ sleepTrackingManager์˜ ๋™์ž‘์„ ์ด์–ดํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์šฐ์„ , foreground service๊ฐ€ ์‹œ์Šคํ…œ์— ์˜ํ•ด ์žฌ์‹œ์ž‘๋˜๋ฉด LifecycleService()์˜ onStartCommand ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ์ด ๋ฉ๋‹ˆ๋‹ค.

์ด๋•Œ, Asleep.hasUnfinishedSession ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜์—ฌ ์ข…๋ฃŒ๋˜์ง€ ์•Š์€ ์„ธ์…˜์ด ์กด์žฌํ•œ๋‹ค๋ฉด, AsleepSDK๋‚ด๋ถ€์— ์ €์žฅ๋œ AsleepConfig๋ฅผ ๊ฐ€์ ธ์™€ sleepTrackingManager๋ฅผ ๋‹ค์‹œ ์ƒ์„ฑํ•˜๊ณ  startSleepTracking์„ ํ˜ธ์ถœํ•˜์—ฌ tracking์„ ์ด์–ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

private var asleepConfig: AsleepConfig? = null
private var sleepTrackingManager: SleepTrackingManager? = null
...
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { 
  super.onStartCommand(intent, flags, startId)

	if(Asleep.hasUnfinishedSession(applicationContext)) {
    asleepConfig = Asleep.getSavedAsleepConfig(applicationContext, BuildConfig.ASLEEP_API_KEY)
    sleepTrackingManager = Asleep.createSleepTrackingManager(asleepConfig, object : SleepTrackingManager.TrackingListener {
			override fun onCreate() {
        ...
      }
      override fun onUpload(sequence: Int) {
      	...
      }
      override fun onClose(sessionId: String) {
      	...
      }
      override fun onFail(errorCode: Int, detail: String) {
      	...
      }
    }
    ...
    sleepTrackingManager?.startSleepTracking()
  }
  ...
}