Android SDK ์์ํ๊ธฐ
Requirements
Minimum requirements on AsleepTrack SDK for Android
- Android 7.0 (API level 24) ํน์ ๋ ๋์ ๋ฒ์
- Java 1.8 ํน์ ๋ ๋์ ๋ฒ์
- Android Gradle plugin 8.0 ํน์ ๋ ๋์ ๋ฒ์
API Key
- AsleepTrack SDK๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ API key๊ฐ ํ์ํฉ๋๋ค.
- API key๋ฅผ ๋ฐ๊ธํ๋ ๋ฐฉ๋ฒ์ ๋ํด์๋ ์ด ๋งํฌ API Key ์์ฑํ๊ธฐ๋ฅผ ์ฐธ๊ณ ํ์ธ์.
Getting Ready
Install AsleepTrack SDK and Settings
- Android Studio๋ฅผ ์ด์ฉํ์ฌ ๊ธฐ๋ณธ ํ๋ก์ ํธ๋ฅผ ์์ฑํฉ๋๋ค.
- AndroidManifest.xml ํ์ผ์ ์ด์ด ํผ๋ฏธ์ ์ ์ถ๊ฐํฉ๋๋ค.
<manifest ...>
<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"/>
<application ...>
...
</application>
</manifest>
- ์ฑ ์์ค์ build.gradle ํ์ผ์ ์ด์ด lifecycle-service, okhttp, gson ๋ฐ asleepsdk๋ฅผ ์ถ๊ฐํฉ๋๋ค.
dependencies {
...
implementation("androidx.lifecycle:lifecycle-service:2.8.7")
implementation("com.squareup.okhttp3:okhttp:4.11.0")
implementation("com.google.code.gson:gson:2.10")
implementation("ai.asleep:asleepsdk:3.1.0")
}
dependencies {
...
implementation 'androidx.lifecycle:lifecycle-service:2.8.7'
implementation 'com.squareup.okhttp3:okhttp:4.11.0'
implementation 'com.google.code.gson:gson:2.10'
implementation 'ai.asleep:asleepsdk:3.1.0'
}
Sleep Tracking with AsleepTrack SDK
๊ถํ ํ๋
- ํ์ ๊ถํ์ธ RECORD_AUDIO์ POST_NOTIFICATIONS(Android 13 ์ด์)์ ํ๋ํฉ๋๋ค.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
ActivityCompat.requestPermissions(
this@MainActivity,
arrayOf(android.Manifest.permission.RECORD_AUDIO,
android.Manifest.permission.POST_NOTIFICATIONS),
0)
...
}
UI ๊ตฌ์ฑ
- ํ๋ฉด์ ๋ณด์ฌ์ง init, begin, end, report์ ๋ฒํผ์ ๋ง๋ญ๋๋ค.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/btn_init"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="200dp"
android:text="init"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_begin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="32dp"
android:text="begin"
app:layout_constraintEnd_toStartOf="@+id/btn_end"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_init" />
<Button
android:id="@+id/btn_end"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:text="end"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/btn_begin"
app:layout_constraintTop_toBottomOf="@+id/btn_init" />
<Button
android:id="@+id/btn_report"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:text="report"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_init" />
</androidx.constraintlayout.widget.ConstraintLayout>
- ๋ฒํผ์ ๋ณ์๋ก ์ ์ธํฉ๋๋ค.
...
private lateinit var btnInit: Button
private lateinit var btnBegin: Button
private lateinit var btnEnd: Button
private lateinit var btnReport: Button
...
btnInit = findViewById(R.id.btn_init)
btnBegin = findViewById(R.id.btn_begin)
btnEnd = findViewById(R.id.btn_end)
btnReport = findViewById(R.id.btn_report)
AsleepTrack SDK ์ด๊ธฐํ
- apiKey์ [YOUR API KEY]์๋ ์ค์ ๋ฐ๊ธ๋ฐ์ apiKey๋ฅผ ์ ๋ ฅํฉ๋๋ค.
- userId๊ฐ null์ธ ๊ฒฝ์ฐ, SDK์์ ์๋ก์ด userId๋ฅผ ์์ฑํฉ๋๋ค.
์ค์ ์ฑ ๊ตฌํ ์, ์์ฑ๋ userId๋ ์ ์ฅ ํ ํ์ ์ ๊ฐ์ ธ์์ ์ ๋ ฅํ๋ ๋ฐฉ์์ผ๋ก ์ฌ์ฉํด์ผ ํฉ๋๋ค. - service๋ ๊ฐ๋ฐํ ์ฑ์ ์๋น์ค๋ช ์ด ์๋ค๋ฉด ์ ๋ ฅํ์ธ์.
- asleepConfigListener๋ ์ด๊ธฐํ์ ์ฑ๊ณต, ์คํจ ์ฌ๋ถ๋ฅผ ์ฝ๋ฐฑํฉ๋๋ค.
- ์ฑ๊ณต์ userId์ asleepConfig๊ฐ ๋ฐํ๋ฉ๋๋ค.
val TAG = "[AsleepSDK]"
private var createdUserId: String? = null
private var createdAsleepConfig: AsleepConfig? = null
private var createdSessionId: String? = null
...
btnInit.setOnClickListener {
Asleep.initAsleepConfig(
context = this,
apiKey = "[YOUR API KEY]",
userId = null,
service = "Test App",
asleepConfigListener = object: Asleep.AsleepConfigListener {
override fun onFail(errorCode: Int, detail: String) {
Log.d(TAG, "initAsleepConfig onFail $errorCode $detail")
}
override fun onSuccess(userId: String?, asleepConfig: AsleepConfig?) {
Log.d(TAG, "initAsleepConfig onSuccess $userId")
createdUserId = userId
createdAsleepConfig = asleepConfig
}
})
}
Begin SleepTracking
- ์๋ฉด ์ธก์ ์ ์์ํฉ๋๋ค.
- initAsleepConfig์์ ์์ฑ๋ asleepConfig์ ์๋ฉด ์ธก์ ์ํ๋ฅผ ํ์ธํ ์ ์๋ asleepTrackingListener๋ฅผ ์ ๋ ฅํฉ๋๋ค.
- ์ธก์ ์ด ์์๋๋ฉด onStart() ์ฝ๋ฐฑ์ด ํธ์ถ๋ฉ๋๋ค.
- ๋งค 30์ด๋ง๋ค onPerform() ์ฝ๋ฐฑ์ด ํธ์ถ๋์ด ์งํ ์ํ๋ฅผ ์ ๊ณตํฉ๋๋ค.
- ์ธก์ ์ด ์ข ๋ฃ๋๋ฉด onFinish() ์ฝ๋ฐฑ์ด ํธ์ถ๋ฉ๋๋ค.
btnBegin.setOnClickListener {
createdAsleepConfig?.let { asleepConfig ->
Asleep.beginSleepTracking(
asleepConfig = asleepConfig,
asleepTrackingListener = object : Asleep.AsleepTrackingListener {
override fun onFail(errorCode: Int, detail: String) {
Log.d(TAG, "beginSleepTracking onFail $errorCode $detail")
}
override fun onFinish(sessionId: String?) {
Log.d(TAG, "beginSleepTracking onFinish $sessionId")
}
override fun onPerform(sequence: Int) {
Log.d(TAG, "beginSleepTracking onPerform $sequence")
}
override fun onStart(sessionId: String) {
Log.d(TAG, "beginSleepTracking onStart $sessionId")
createdSessionId = sessionId
}
})
}
}
End SleepTracking
- ์๋ฉด ์ธก์ ์ ์ข ๋ฃํฉ๋๋ค.
btnEnd.setOnClickListener {
Asleep.endSleepTracking()
}
Get Report
- ์ธก์ ๋ ์๋ฉด ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ต๋๋ค.
- initAsleepConfig์์ ์์ฑ๋ asleepConfig๋ฅผ ํ๋ผ๋ฉํฐ๋ก ์ ๋ ฅํ์ฌ Reports ๊ฐ์ฒด๋ฅผ ๋ง๋ญ๋๋ค.
- ์๋ฉด ์ธก์ ์ ์์ฑ๋ sessionId๋ฅผ ์ ๋ ฅํฉ๋๋ค.
btnReport.setOnClickListener {
createdSessionId?.let { sessionId ->
val reports = Asleep.createReports(createdAsleepConfig)
reports?.getReport(
sessionId = sessionId,
reportListener = object : Reports.ReportListener {
override fun onFail(errorCode: Int, detail: String) {
Log.d(TAG, "getReport onFail $errorCode $detail")
}
override fun onSuccess(report: Report?) {
Log.d(TAG, "getReport onSuccess $report")
}
})
}
}
Logcat ํ์ธ
- logcat ๋ก๊ทธ์ ์๋์ ๊ฐ์ด userId, sessionId, report๊ฐ ์ฑ๊ณต์ ์ผ๋ก ์์ฑ๋์๋ค๋ฉด, SDK๊ฐ ์ ์์ ์ผ๋ก ๋์ํ๊ณ ์๋ ๊ฒ์ ๋๋ค.
์ฑ ๊ฐ๋ฐ์ ์ฃผ์์
- ํฌ๊ทธ๋ผ์ด๋ ์๋น์ค ์ฌ์ฉ
์๋ฉด ์ธก์ ์ ์ํด, ์ฑ์ ๋ฐค ๋์ ์ง์์ ์ธ ๋ ์ฝ๋ฉ, ๋ฐ์ดํฐ ์ฒ๋ฆฌ, ๋คํธ์ํฌ ์์ ๋ฑ์ ์ํํด์ผ ํฉ๋๋ค. ์ด๋ฌํ ์์ ์ด ์ค๋จ๋์ง ์๋๋ก ํ๊ธฐ ์ํด, ํฌ๊ทธ๋ผ์ด๋ ์๋น์ค(Foreground Service)๋ฅผ ํ์ฉํ์ฌ ์ฑ์ ๊ฐ๋ฐํ๋ ๊ฒ์ด ํ์์ ์ ๋๋ค.
๋ ์์ธํ ๋ด์ฉ์ Android ํฌ๊ทธ๋ผ์ด๋ ์๋น์ค ๊ฐ์ด๋๋ฅผ ์ฐธ์กฐํ์ธ์.
์ ํฌ SDK๋ ๋ ๊ฐ์ง ๊ฐ๋ฐ ๋ฐฉ์์ ์ง์ํฉ๋๋ค
- Begin-End ๋ฐฉ์
ํฌ๊ทธ๋ผ์ด๋ ์๋น์ค๋ฅผ SDK ๋ด๋ถ์ ํฌํจํ์ฌ, ์๋ฉด ์ธก์ ๊ณผ ๊ด๋ จ๋ ๋ชจ๋ ์์ ์ ์ถ์ํํ์ต๋๋ค.
๊ฐ๋ฐ์๋ ํฌ๊ทธ๋ผ์ด๋ ์๋น์ค๋ฅผ ์ฑ์์ ๊ตฌํํ ํ์๊ฐ ์๊ณ beginSleepTracking() ๋ฐ endSleepTracking() ํจ์๋ง ํธ์ถํ์ฌ ๊ฐํธํ๊ฒ ์๋ฉด ์ธก์ ๊ธฐ๋ฅ์ ๊ตฌํํ ์ ์์ต๋๋ค. Begin-End ๋ฐฉ์ ์ํ ์ฑ์ ์ฐธ๊ณ ํ์ธ์.- SleepTrackingManager ๋ฐฉ์
ํฌ๊ทธ๋ผ์ด๋ ์๋น์ค๋ฅผ ์ง์ ๊ตฌํํ์ฌ ๋ ๋ง์ ์ ์ด๋ฅผ ์ ๊ณตํ๋ ๋ฐฉ์์ ๋๋ค.
์ด ๋ฐฉ์์ ๋ค์ ๋ณต์กํ ์ ์์ผ๋, ์ฌ๋ฆฝ ์คํ ์ด์ง(Sleep Stage)์ ๋ฐ๋ผ ํ๋์จ์ด๋ฅผ ์ ์ดํ๊ฑฐ๋ ํน์ ์ปค์คํ ์์ ์ด ํ์ํ ๊ฒฝ์ฐ ์ ํฉํฉ๋๋ค. FGS ํ๋ก์ธ์ค ๋ถ๋ฆฌ ์ํ ์ฑ์ ์ฐธ๊ณ ํ์ธ์.
๊ฐ๋ฐํ๋ ค๋ ์ฑ์ ์๊ตฌ ์ฌํญ์ ๋ฐ๋ผ ์ ํฉํ ๋ฐฉ์์ ์ ํํ์ธ์.- ์ธ์ฑ ์ ๋ฐ์ดํธ ๊ธฐ๋ฅ ๊ถ์ฅ ์ธ์ฑ ์ ๋ฐ์ดํธ ๊ฐ์ด๋
- ์๋๋ก์ด๋ ์ฌ์ฉ์ ์ค์ ์ ๋ฐ๋ผ ์๋ฉด์ธก์ ์์ ํ ์ฑ ์ ๋ฐ์ดํธ๊ฐ ์๋์ผ๋ก ์ด๋ค์ง๊ฒ ๋๋ฉด ์๋์น ์์ ์ฑ ์ข ๋ฃ๋ก ์๋ฉด ์ธก์ ์ค ๋น์ ์ ์ข ๋ฃ๊ฐ ๋ฐ์ํ๊ฒ ๋ฉ๋๋ค. ์ด๋ฌํ ์ํฉ์ ๋ฐฉ์งํ๊ณ ์ ๋ฏธ๋ฆฌ ์ ๋ฐ์ดํธ๋ฅผ ๋ฐ์ ์ ์๋ ์ธ์ฑ ์ ๋ฐ์ดํธ๋ผ๋ ๊ธฐ๋ฅ์ ์ฌ์ฉํ๊ฒ ๋๋ฉด ์๋์น ์์ ์ํฉ์ ์ค์ผ ์ ์์ต๋๋ค.
- ๋ฐฐํฐ๋ฆฌ ์ต์ ํ ์์ธ ๊ถํ ์๋ด doze-standby
- ์๋ฉด ์ธก์ ์ ์์ ์ฑ์ ๋ณด์ฅํ๊ธฐ ์ํด ๋ฐฐํฐ๋ฆฌ ์ต์ ํ ์์ธ ๊ถํ์ด ํ์ํฉ๋๋ค.
์ด ๊ถํ์ ์๋ฉด ์ธก์ ์ค Doze ๋ชจ๋๋ก ์ ํ๋๋ ๊ฒ์ ๋ฐฉ์งํ๊ธฐ ์ํด ์ฌ์ฉ๋๋ฉฐ, REQUEST_IGNORE_BATTERY_OPTIMIZATIONS ๊ถํ์ ํตํด ์ค์ ๋ฉ๋๋ค.
Updated 11 days ago
Whatโs Next