1. 시작하기 전에
이 Codelab에서는 SociaLite용 앱 위젯을 만드는 과정을 안내합니다. 먼저 간단한 Glance 위젯을 빌드하여 SociaLite와 홈 화면에 추가합니다. 다음으로, Glance 구성요소와 Glance 테마를 사용하여 위젯에 0 상태를 추가합니다. 그런 다음 위젯이 즐겨찾는 연락처를 선택하는 사용자 상호작용을 지원하도록 설정합니다. 마지막으로, 앱에서 위젯을 업데이트하는 방법을 알아봅니다.
기본 요건
- 기본적인 Kotlin 지식
- Android 스튜디오 설정 Codelab 완료 또는 Android 스튜디오 사용 방법과 Android 15 에뮬레이터나 Android 15를 실행하는 실제 기기에서 앱을 테스트하는 방법에 관한 지식
- 기본적인 Hilt 지식
- 기본적인 Compose 지식 Glance는 Jetpack Compose의 컴포저블을 사용하지 않으나, 프레임워크와 코딩 스타일은 그대로 사용합니다.
이 Codelab에서 학습할 내용
- 앱이 위젯을 지원하도록 구성하는 방법
- Glance 구성요소를 사용하여 반응형 레이아웃을 빌드하는 방법
GlanceTheme
를 사용하여 사용자의 홈 화면에서 동적 색상을 지원하는 방법- 위젯에서 사용자 상호작용을 처리하는 방법
- 앱에서 위젯을 업데이트하는 방법
필요한 항목
- Android 스튜디오 최신 버전
- Android 12 및 이후 버전을 실행하는 테스트 기기나 에뮬레이터
- Android 12 및 이후 버전 SDK
2. 설정
시작 코드 가져오기
- 'Android 15에서 더 넓은 화면 시행 처리' Codelab 또는 '뒤로 탐색 예측 애니메이션 추가' Codelab을 완료한 경우 이미 시작 코드가 있으므로 위젯 추가 섹션으로 건너뜁니다.
- GitHub에서 시작 코드를 다운로드합니다.
저장소를 클론하여 codelab_improve_android_experience_2024 브랜치를 체크아웃해도 됩니다.
git clone git@github.com:android/socialite.git
cd socialite
git checkout codelab_improve_android_experience_2024
- Android 스튜디오에서 SociaLite를 열고 Android 15 기기나 에뮬레이터에서 앱을 실행합니다. 화면이 다음과 같이 표시됩니다.
동작 탐색이 포함된 SociaLite
3. 위젯 추가
위젯이란?
위젯은 다른 Android 앱 내부에 삽입할 수 있는 앱의 한 부분입니다. 이때 가장 일반적인 다른 Android 앱은 사용자의 홈 화면입니다.
앱에 위젯을 추가하면 사용자가 빠르게 일반적인 작업을 시작하고, 한눈에 파악이 가능한 정보를 보고, 앱의 콘텐츠로 기기를 맞춤설정할 수 있습니다.
Glance란?
Jetpack Glance는 Kotlin으로 Compose와 같은 API를 사용하여 위젯을 작성하기 위한 라이브러리입니다. Jetpack Glance는 리컴포지션, Kotlin으로 작성된 선언형 UI 코드, 규칙 중심 구성요소 등 Compose와 동일한 여러 이점을 갖습니다. Glance를 사용하면 위젯에서 XML 원격 뷰를 사용해야 할 필요성이 크게 줄어듭니다.
위젯 만들기
Android의 위젯은 AndroidManifest
안에 <receiver>
요소로 선언됩니다. 이 리시버는 내보내기되고, android.appwidget.action.APPWIDGET_UPDATE
작업 인텐트를 처리하고, 이름이 android.appwidget.provider
인 메타데이터 요소를 통해 앱 위젯 설정 파일을 제공해야 합니다.
SociaLite에 위젯 추가
Socialite에 사용자가 즐겨찾는 연락처를 보고 이 연락처가 보낸 읽지 않은 메시지가 있는지 확인할 수 있는 위젯을 추가해 보겠습니다. 읽지 않은 메시지가 있는 경우 위젯을 탭하면 즐겨찾는 연락처의 채팅으로 이동합니다. 이에 더해 Glance 구성요소와 테마를 사용하여 위젯에 반응형 디자인과 동적 색상도 적용할 것입니다.
시작하려면 Socialite에 정적 "Hello World" 위젯을 추가합니다. 뒤에서 위젯의 기능을 확장할 것입니다.
다음을 실행합니다.
- 앱에 Glance 종속 항목을 추가합니다.
GlanceAppWidget
의 구현을 만듭니다.GlanceAppWidgetReceiver
를 만듭니다.- 앱 위젯 XML 파일을 사용하여 위젯을 구성합니다.
AndroidManifest.xml
파일에 리시버 및 앱 위젯 정보를 추가합니다.
프로젝트에 Glance 추가
시작 코드가 SociaLite의 버전 카탈로그 libs.versions.toml
에 Glance 버전 및 라이브러리 위치를 추가했습니다.
libs.versions.toml
[versions]
//..
glance = "1.1.0-beta02"
[libraries]
glance-appwidget = { group = "androidx.glance", name = "glance-appwidget", version.ref = "glance" }
glance-material = { group = "androidx.glance", name = "glance-material3", version.ref = "glance" }
이에 더해 SociaLite의 app/build.gradle.kts
파일에 Glance의 종속 항목이 포함되어 있습니다.
build.gradle.kts
dependencies {
...
implementation(libs.glance.appwidget)
implementation(libs.glance.material)
...
}
- 이러한 파일을 수정했다면 프로젝트를 동기화하여 Glance 라이브러리를 다운로드하세요.
GlanceAppWidget
및 GlanceAppWidgetReceiver
만들기
Android는 broadcast receiver를 사용하여 SociaLite에 위젯이 추가되었거나 위젯에 업데이트가 필요하거나 위젯이 삭제되었음을 알립니다. Glance는 추상 리시버 클래스 GlanceAppWidgetReceiver를 제공하며, GlanceAppWidgetReceiver는 AppWidgetProvider를 확장합니다.
GlanceAppWidgetReceiver
구현도 GlanceAppWidget
의 인스턴스를 제공하는 일을 담당합니다. 이 클래스는 Glance의 컴포저블을 원격 뷰로 렌더링합니다.
시작 코드에는 GlanceAppWidget
을 확장하는 SocialiteAppWidget
클래스와 GlanceAppWidgetReceiver
를 확장하는 SocialiteAppWidgetReceiver
클래스, 이렇게 두 개의 클래스가 있습니다.
시작하려면 다음 단계를 따릅니다.
app/src/main/java/com/google/android/samples/socialite/
의widget
패키지로 이동합니다.SociaLiteAppWidget
클래스를 엽니다. 이 클래스는provideGlance
메서드를 재정의합니다.TODO
를provideContent
에 대한 호출로 바꾸고 위젯의 컴포저블 함수를 매개변수로 전달합니다. 지금은 위젯이Hello World
메시지만 표시하지만, 이 Codelab의 뒷부분에서 더 많은 기능을 추가할 것입니다.
package com.google.android.samples.socialite.widget
import android.content.Context
import androidx.glance.GlanceId
import androidx.glance.GlanceTheme
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.provideContent
import androidx.glance.text.Text
class SociaLiteAppWidget : GlanceAppWidget() {
override suspend fun provideGlance(context: Context, id: GlanceId) {
provideContent {
GlanceTheme {
Text("Hello World")
}
}
}
}
widget
패키지의SociaLiteAppWidgetReceiver
클래스를 엽니다. 지금은 리시버가SociaLiteWidget
의 인스턴스를 제공하지만, 이어지는 섹션에서 더 많은 기능을 추가할 것입니다.TODO
를SociaLiteAppWidget()
생성자로 바꿉니다.
package com.google.android.samples.socialite.widget
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.GlanceAppWidgetReceiver
class SociaLiteAppWidgetReceiver : GlanceAppWidgetReceiver() {
override val glanceAppWidget: GlanceAppWidget = SociaLiteAppWidget()
}
이제 Android가 위젯을 표시하고 사용자가 홈 화면에 위젯을 추가할 수 있도록 구성할 준비가 되었습니다.
appwidget-provider 정보 추가
- res/xml > New > XML 리소스 파일을 마우스 오른쪽 버튼으로 클릭합니다.
- 파일 이름으로 socialite_widget_info를 입력하고 루트 요소로 appwidget-provider를 입력한 다음 OK를 클릭합니다. 이 파일은
AppWidgetHost
가 처음으로 위젯을 표시하는 데 사용되는appwidget
메타데이터를 포함합니다. - socialite_widget_info.xml 파일에 다음 코드를 추가합니다.
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:resizeMode="horizontal|vertical"
android:updatePeriodMillis="3600000"
android:minHeight="128dp"
android:minWidth="128dp"
android:minResizeHeight="128dp"
android:minResizeWidth="128dp"
android:configure="com.google.android.samples.socialite.widget.SociaLiteAppWidgetConfigActivity"
android:widgetFeatures="configuration_optional|reconfigurable"
android:previewImage="@drawable/widget_preview"
android:maxResizeHeight="512dp"
android:maxResizeWidth="512dp"
android:targetCellWidth="2"
android:targetCellHeight="2"
android:initialLayout="@layout/glance_default_loading_layout">
</appwidget-provider>
다음 표에는 이 코드의 속성과 각각에 대한 설명이 정리되어 있습니다.
속성 이름 | 설명 |
| 위젯의 크기를 세로 방향과 가로 방향으로 조절할 수 있습니다. |
| 위젯을 홈 화면에 추가할 때 기본 크기를 지정합니다. |
| 위젯 호스트가 위젯을 새로고침하는 시점을 제어합니다. 앱이 실행 중일 때 언제든지 그리고 표시할 새 정보가 있을 때 위젯을 업데이트할 수 있습니다. |
| 위젯의 크기를 얼마나 작게 조절할 수 있는지 설정합니다. |
| 위젯을 홈 화면에 추가할 때의 최소 기본 크기를 지정합니다. |
| Glance가 컴포저블을 렌더링하는 동안 표시되는 초기 레이아웃을 제공합니다. |
| 위젯 선택 도구에 표시할 위젯의 정적 이미지를 제공합니다. |
| 위젯이 지원하는 여러 기능을 나타냅니다. 위젯 호스트에게 제공되는 힌트이며, 위젯의 행동을 실제로 변경하지 않습니다. |
| 구성 활동 클래스의 이름입니다. 나중에 위젯을 구성하는 활동입니다. |
API 31 이상의 기능을 포함하여 사용 가능한 모든 속성을 보려면 AppWidgetProviderInfo
를 참고하세요.
AndroidManifest
업데이트 및 테스트
이제 AndroidManifest.xml
파일을 업데이트하고 위젯을 테스트할 준비가 되었습니다. 파일에 있는 application
요소의 하위 요소로 receiver
요소를 정의합니다. 이 리시버는 APPWIDGET_UPDATE
인텐트를 처리하고 Android 런처에 appwidget
메타데이터를 제공합니다.
시작하려면 다음 단계를 따릅니다.
SociaLiteAppWidgetReceiver
의 내보내기를 위한receiver
요소를 만듭니다. 다음을 복사하여AndroidManifest.xml
파일의application
요소 뒤에 붙여넣습니다.
<receiver
android:name=".widget.SociaLiteAppWidgetReceiver"
android:exported="true"
android:label="Favorite Contact">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/socialite_widget_info" />
</receiver>
- 앱을 컴파일하고 실행합니다.
- 앱이 실행 중인 상태에서 위젯을 홈 화면에 추가합니다. 예를 들어, Pixel에서는 배경을 길게 누르고 위젯 > SociaLite를 선택합니다. 이렇게 하면 홈 화면에 위젯을 추가할 수 있을 것입니다.
위젯은 "Hello World" 메시지를 표시하며 투명 배경을 갖습니다. 이 위젯은 아직 보기에 좋지도, 유용한 기능을 제공하지도 않습니다. 다음 섹션에서는 보다 복잡한 레이아웃을 추가하고 Material Design 색상을 사용하여 위젯을 꾸밉니다.
4. 디자인 개선
지금까지 정적 위젯을 하나 만들었습니다. 이 위젯에는 좋은 위젯의 특징들이 결여되어 있습니다. 좋은 위젯은 다음과 같은 특징을 갖습니다.
- 최신 콘텐츠를 간략하게 표시하고 간단한 기능을 제공합니다.
- 크기 조절이 가능한 레이아웃을 통해 부자연스러운 공백을 최소화합니다.
- 앱 위젯 호스트 배경의 색상을 적용합니다.
좋은 위젯의 특징을 자세히 알아보려면 위젯을 참고하세요.
Scaffold 추가
이제 Glance Scaffold
구성요소 안에 표시되도록 위젯을 업데이트하겠습니다.
Scaffold
는 Glance 라이브러리에서 제공합니다. TitleBar
가 있는 위젯 UI를 표시하기 위한 간단한 슬롯 API입니다. Scaffold는 배경 색상을 GlanceTheme.colors.widgetBackground
로 설정하고 패딩을 적용합니다. Scaffold는 최상위 구성요소가 됩니다.
시작하려면 다음 단계를 따릅니다.
SociaLiteAppWidget
의 구현을 다음 코드로 바꿉니다.
package com.google.android.samples.socialite.widget
import android.content.Context
import androidx.compose.runtime.Composable
import androidx.glance.GlanceId
import androidx.glance.GlanceModifier
import androidx.glance.GlanceTheme
import androidx.glance.ImageProvider
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.components.Scaffold
import androidx.glance.appwidget.components.TitleBar
import androidx.glance.appwidget.provideContent
import androidx.glance.layout.fillMaxSize
import androidx.glance.text.Text
import com.google.android.samples.socialite.R
class SociaLiteAppWidget : GlanceAppWidget() {
override suspend fun provideGlance(context: Context, id: GlanceId) {
provideContent {
GlanceTheme() {
Content()
}
}
}
@Composable
private fun Content() {
Scaffold(titleBar = {TitleBar(startIcon = ImageProvider(R.drawable.ic_launcher_monochrome), title = "SociaLite")},
modifier = GlanceModifier.fillMaxSize()) {
Text("Hello World")
}
}
}
- 업데이트를 보려면 앱을 다시 실행한 다음 홈 화면에 위젯을 새로 추가합니다.
위젯은 외부 호스트에 의해 표시되는 원격 뷰라는 사실을 기억하세요. 뒷부분에서는 앱 내부에서 위젯을 자동으로 업데이트하는 기능을 추가할 것입니다. 그때까지는 코드 변경이 적용된 것을 보기 위해 위젯 선택 도구에서 위젯을 추가해야 합니다.
아까보다는 나아졌지만, 이 위젯을 다른 위젯들과 비교해 보면 색상이 좀 어색합니다. 홈 화면에 표시되는 위젯은 일반적으로 사용자의 테마 설정에 따라 색상을 설정합니다. 동적 색상 토큰을 사용하면 위젯의 테마가 기기의 배경화면과 테마에 따라 바뀌도록 할 수 있습니다.
동적 색상 추가
이번에는 Scaffold 배경에 widgetBackground
색상 토큰을 추가하고 TitleBar
텍스트 및 Text 구성요소에 onSurface
색상 토큰을 추가합니다. 텍스트 스타일을 업데이트하려면 TextStyle
Glance 클래스를 가져와야 합니다. Scaffold 배경을 업데이트하려면 Scaffold
의 backgroundColor
속성을 GlanceTheme.colors.widgetBackground
로 설정해야 합니다.
시작하려면 다음 단계를 따릅니다.
- SociaLiteAppWidget.kt 파일에 새 가져오기를 포함합니다.
//Add to the imports section of your Kotlin code.
import androidx.glance.text.TextStyle
Content
컴포저블에widgetBackground
를 추가합니다.
Scaffold(
titleBar = {
TitleBar(
textColor = GlanceTheme.colors.onSurface,
startIcon = ImageProvider(R.drawable.ic_launcher_monochrome),
title = "SociaLite",
)
},
backgroundColor = GlanceTheme.colors.widgetBackground,
modifier = GlanceModifier.fillMaxSize(),
) {
Text(text = "Hello World", style = TextStyle(color = GlanceTheme.colors.onSurface))
}
- 업데이트를 보려면 앱을 다시 실행한 다음 홈 화면에 위젯을 새로 추가합니다.
이제 홈 화면에 있는 다른 위젯들과 테마가 일치하며, 배경을 변경하거나 어두운 모드를 설정하면 자동으로 색상이 업데이트됩니다. 배경에 여러 색상이 있는 경우 위젯은 배경에서 자신이 놓여 있는 부분의 색상에 맞게 바뀝니다.
|
|
0 상태 추가
이번에는 상태와 위젯 구성에 대해 생각해 보아야 합니다. 홈 화면에 추가된 위젯을 구성해야 하는 경우에는 0 상태를 표시하는 것이 좋습니다. 0 상태는 사용자에게 위젯을 구성하도록 요청합니다. 구성 활동을 위젯에 추가한 다음 0 상태에서 구성 활동에 연결합니다.
이 Codelab에서는 위젯의 구성 상태를 저장, 액세스, 수정하는 클래스를 제공합니다. 위젯의 UI가 이 상태를 표시하도록 업데이트하는 코드를 추가한 다음 사용자 탭 작업을 처리하는 람다 작업을 만들겠습니다.
위젯 모델 검토
com.google.android.samples.socialite.widget.model
패키지에 들어 있는 클래스를 검토해 보세요.
WidgetModel
클래스, WidgetModelDao
클래스와 WidgetModelRepository
클래스가 있습니다. 이러한 클래스는 Codelab 시작 코드에 이미 있으며, 위젯의 상태를 기본 Room
데이터베이스에 유지하는 작업을 처리합니다. 또한 Hilt를 사용하여 수명 주기를 관리합니다.
WidgetModel
클래스는 Android에 의해 할당된 widgetId
, 표시할 SociaLite 연락처의 contactId
, 표시할 displayName
및 photo
, 그리고 해당 연락처에게서 온 읽지 않은 메시지가 있는 경우 불리언을 포함합니다. 이는 SociaLiteAppWidget
컴포저블에 의해 소비되어 위젯에 표시됩니다.
WidgetModelDao
는 SociaLite 데이터베이스에 대한 액세스를 추상화하는 데이터 액세스 객체입니다. WidgetModelRepository
는 WidgetModel
인스턴스를 생성, 읽기, 업데이트, 삭제하는 편의 함수를 제공합니다. 이러한 클래스는 Hilt에 의해 생성되고 종속 항목 삽입을 통해 앱에 삽입됩니다.
app/src/main/java/com/google/android/samples/socialite/widget/model/
에 있는model
패키지의WidgetModel.kt
파일을 엽니다.
이는 Entity
주석이 있는 data
클래스입니다. 각 위젯 인스턴스에는 Android에 의해 전용 ID가 할당되며, 이 ID는 SociaLite가 모델 데이터의 기본 키로 사용합니다. 모델의 각 인스턴스는 연결된 연락처의 기본 정보와 이 연락처가 보낸 읽지 않은 메시지가 있는지 여부를 추적합니다.
@Entity(
foreignKeys = [
ForeignKey(
entity = Contact::class,
parentColumns = ["id"],
childColumns = ["contactId"],
onDelete = ForeignKey.CASCADE,
),
],
indices = [
Index("widgetId"),
Index("contactId"),
],
)
data class WidgetModel(
@PrimaryKey val widgetId: Int,
val contactId: Long,
val displayName: String,
val photo: String,
val unreadMessages: Boolean = false,
) : WidgetState
0 상태
Content
컴포저블이 WidgetModelRepository
에서 위젯의 모델을 로드한 다음 사용 가능한 모델이 없으면 0 상태를 표시하고 있으면 위젯의 정규 콘텐츠를 표시하도록 해야 합니다. 이 시점에서는 정규 콘텐츠가 "Hello World" 메시지이지만, 이어지는 파트에서 더 나은 인터페이스를 만들 것입니다.
Content
컴포저블을 ZeroState
컴포저블 또는 Text
자리표시자를 표시하는 when
표현식으로 바꿉니다.
- 컴포저블 외부에 있는
provideGlance
메서드에서WidgetModelRepository
및 현재 위젯 ID에 대한 참조를 가져옵니다SociaLiteAppWidget
provideGlance
메서드에서provideContent
앞에 다음 줄을 추가합니다.
override suspend fun provideGlance(context: Context, id: GlanceId) {
val widgetId = GlanceAppWidgetManager(context).getAppWidgetId(id)
val repository = WidgetModelRepository.get(context)
다음 가져오기를 추가해야 할 수도 있습니다.
import com.google.android.samples.socialite.widget.model.WidgetModel
import com.google.android.samples.socialite.widget.model.WidgetModelRepository
import com.google.android.samples.socialite.widget.model.WidgetState.Loading
import androidx.glance.appwidget.GlanceAppWidgetManager
Content
컴포저블 함수에서 저장소와 위젯 ID를 매개변수로 추가한 다음 이를 사용하여 모델을 로드합니다.Content
컴포저블의 함수 서명을 업데이트하고 다음 줄을 추가합니다.
private fun Content(repository: WidgetModelRepository, widgetId: Int) {
val model = repository.loadModel(widgetId).collectAsState(Loading).value
- Android 스튜디오가 다음 가져오기를 자동으로 추가하지 않는다면 수동으로 추가합니다.
import androidx.compose.runtime.collectAsState
이에 더해 provideGlance
가 Content
에 위젯 ID와 저장소를 전달하도록 업데이트해야 합니다.
provideGlance
를 다음으로 바꿉니다.
override suspend fun provideGlance(context: Context, id: GlanceId) {
val widgetId = GlanceAppWidgetManager(context).getAppWidgetId(id)
val repository = WidgetModelRepository.get(context)
provideContent {
GlanceTheme {
Content(repository, widgetId)
}
}
}
Content
컴포저블 함수에서 모델이 있는지 여부에 따라 어느 상태를 표시할지 정합니다.Scaffold
구성요소와 그 콘텐츠를 다음 when 블록으로 바꾸어서Scaffold
와 위젯 콘텐츠를ZeroState
컴포저블 안으로 이동합니다.
when (model) {
is WidgetModel -> {Text("Hello World")}
else -> ZeroState(widgetId)
}
ZeroState
컴포저블은 시작 코드의 com.google.android.samples.socialite.widget.ui
패키지에 이미 포함되어 있습니다.
- Android 스튜디오가
com.google.android.samples.socialite.widget.ui
패키지를 자동으로 가져오지 않는다면SociaLiteAppWidget
의 import 섹션에 다음 코드를 추가합니다.
import com.google.android.samples.socialite.widget.ui.ZeroState
- 업데이트를 보려면 앱을 다시 실행한 다음 홈 화면에 위젯을 새로 추가합니다. 위젯에 ZeroState 구성요소와 버튼이 표시되는 것을 볼 수 있습니다. 버튼을 클릭하면 구성 활동이 열립니다. 다음 섹션에서는 이 활동으로부터 위젯 상태를 업데이트할 것입니다.
구성 활동
ZeroState 컴포저블 활동을 검토합니다. 이 함수는 com.google.android.samples.socialite.widget.ui
패키지의 ZeroState.kt
파일에 있습니다.
@Composable fun ZeroState(widgetId: Int) { val widgetIdKey = ActionParameters.Key<Int>(AppWidgetManager.EXTRA_APPWIDGET_ID) Scaffold( titleBar = { TitleBar( modifier = GlanceModifier.clickable(actionStartActivity(MainActivity::class.java)), textColor = GlanceTheme.colors.onSurface, startIcon = ImageProvider(R.drawable.ic_launcher_monochrome), title = "SociaLite", ) }, backgroundColor = GlanceTheme.colors.widgetBackground, modifier = GlanceModifier.fillMaxSize(), ) { Box(modifier = GlanceModifier.fillMaxSize(), contentAlignment = Alignment.Center) { Button( text = "Select Favorite Contact", onClick = actionStartActivity<SociaLiteAppWidgetConfigActivity>( parameters = actionParametersOf(widgetIdKey to widgetId), ), ) } } }
Scaffold
컴포저블은 ZeroState
컴포저블 안으로 이동되었습니다. TitleBar
에는 clickable
수정자가 있으며, 이 수정자는 SociaLite의 기본 활동을 엽니다. ZeroState
는 Glance Button
컴포저블을 사용하여 사용자에게 클릭 유도 문구를 표시하고, 클릭되면 SociaLiteAppWidgetConfigActivity
활동을 열고 인텐트 추가 항목으로 위젯 ID를 포함합니다. 두 작업 모두 Glance의 actionStartActivity
편의 함수를 사용합니다. 작업에 관해 자세히 알아보려면 사용자 상호작용 처리를 참고하세요.
- 위젯의 구성을 업데이트하는 데
SociaLiteAppWidgetConfigActivity
가 사용되는 것을 살펴봅니다. 이 클래스는 위젯의 구성 활동이기도 합니다. 구성 활동은AppWidgetManager.
*EXTRA_APPWIDGET_ID.
* 키를 사용하여 인텐트 정수 추가 항목을 읽습니다. 구성 활동에 관해 자세히 알아보려면 사용자가 앱 위젯을 구성하도록 사용 설정을 참고하세요. SociaLiteAppWidgetConfigActivity
에서ContactRow
onClick
속성의TODO
를 다음 코드로 바꿉니다.
{
coroutineScope.launch {
widgetModelRepository.createOrUpdate(
WidgetModel(
appWidgetId,
contact.id,
contact.name,
contact.iconUri.toString(),
false,
),
)
SociaLiteAppWidget().updateAll(this@SociaLiteAppWidgetConfigActivity)
val resultValue = Intent().putExtra(
AppWidgetManager.EXTRA_APPWIDGET_ID,
appWidgetId,
)
setResult(RESULT_OK, resultValue)
finish()
}
}
Android 스튜디오가 자동으로 추가하지 않는다면 다음 include를 수동으로 추가합니다.
import com.google.android.samples.socialite.widget.model.WidgetModel
import androidx.glance.appwidget.updateAll
import kotlinx.coroutines.launch
이 코드 블록은 위젯 상태를 업데이트합니다. 먼저 저장소를 사용하여 WidgetModel
을 저장하거나 선택된 연락처의 정보로 업데이트합니다. 다음으로, updateAll
정지 함수를 호출합니다. 이 함수는 홈 화면의 모든 위젯을 업데이트하며, 앱의 모든 위치에서 호출할 수 있습니다. 마지막으로, 이 블록은 위젯을 성공적으로 업데이트했음을 나타내도록 구성 활동의 결과를 설정합니다.
- 앱을 실행하고 홈 화면에 위젯을 새로 추가합니다. 새 0 상태를 볼 수 있을 것입니다.
- Select favorite contact를 클릭합니다. 그러면 구성 활동이 열립니다.
- 연락처를 하나 선택합니다. 그러면 위젯이 업데이트됩니다. 그러나 위젯은 아직 즐겨찾는 연락처를 표시하지 않습니다. 이 기능은 다음 섹션에서 추가할 것입니다.
위젯 데이터 관리
- App Inspection 도구를 열고 필요한 경우 프로세스에 연결한 다음 Database Inspector 탭을 선택하여 앱 데이터베이스의 콘텐츠를 살펴봅니다.
- 위젯에서 즐겨찾는 연락처를 하나 선택하여 "Hello World"로 업데이트되는지 살펴봅니다. App Inspection 도구로 돌아가면 WidgetModel 탭에 위젯 항목 하나가 있는 것을 볼 수 있을 것입니다. 변경사항을 보려면 테이블을 새로고침하거나 Live updates를 눌러야 할 수 있습니다.
- 위젯을 하나 더 추가하고 다른 연락처를 선택합니다. 새 모델을 보려면 Refresh table 또는 Live updates를 눌러야 할 수 있습니다.
- 위젯을 삭제한 다음, 위젯이 삭제된 후에도 데이터베이스에 모델이 남아 있는 것을 봅니다.
onDeleted
를 재정의하여 위젯이 삭제되면 데이터베이스가 정리되도록 SociaLiteAppWidgetReceiver
를 업데이트할 수 있습니다.
분리된 위젯 모델을 정리하려면 WidgetModelRepository.cleanupWidgetModels
를 호출하면 됩니다. 이 저장소 클래스는 Hilt에 의해 관리되며, 인스턴스에 액세스하려면 종속 항목 삽입을 사용해야 합니다.
SociaLiteAppWidgetReceiver
에서 리시버 클래스 선언에AndroidEntryPoint
Hilt 주석을 추가하고WidgetModelRepository
인스턴스를 삽입합니다.onDeleted
의 메서드 재정의에서WidgetModelRepository.cleanupWidgetModels
를 호출합니다.
코드는 다음과 같습니다.
package com.google.android.samples.socialite.widget
import android.content.Context
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.GlanceAppWidgetReceiver
import com.google.android.samples.socialite.widget.model.WidgetModelRepository
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
@AndroidEntryPoint
class SociaLiteAppWidgetReceiver : GlanceAppWidgetReceiver() {
override val glanceAppWidget: GlanceAppWidget = SociaLiteAppWidget()
@Inject
lateinit var repository: WidgetModelRepository
override fun onDeleted(context: Context, appWidgetIds: IntArray) {
super.onDeleted(context, appWidgetIds)
repository.cleanupWidgetModels(context)
}
}
- 앱을 다시 실행합니다. 이제 홈 화면에서 위젯이 삭제되면App Inspector에서 모델 행이 삭제되는 것을 볼 수 있습니다.
5. 연락처 UI 추가 및 새 메시지 수신 시 연락처 UI 업데이트
이 Codelab의 마지막 섹션을 시작하겠습니다. 이 섹션에서는 위젯의 최종 연락처 UI를 구현하고 해당 연락처의 읽지 않은 메시지가 있는 경우 UI를 업데이트합니다.
- 모델 패키지의
WidgetModelRepository
클래스를 검토합니다.
여기에 있는 updateUnreadMessagesForContact
편의 메서드는 연락처의 ID에 연결된 위젯을 업데이트합니다.
//Don't add this code.
fun updateUnreadMessagesForContact(contactId: Long, unread: Boolean) {
coroutineScope.launch {
widgetModelDao.modelsForContact(contactId).filterNotNull().forEach { model ->
widgetModelDao.update(
WidgetModel(model.widgetId, model.contactId, model.displayName, model.photo, unread)
)
SociaLiteAppWidget().updateAll(appContext)
}
}
}
이 메서드에는 업데이트되는 연락처의 ID인 contactId
와 읽지 않은 메시지 상태를 나타내는 불리언인 unread
, 이렇게 두 개의 매개변수가 있습니다. 이 메서드는 WidgetModelDao
를 사용하여 이 연락처를 표시하는 모든 위젯을 찾은 다음 새 읽기 상태로 모델을 업데이트합니다. 그런 다음 Glance에서 제공하는 SociaLiteAppWidget().updateAll
메서드를 호출하여 사용자의 홈 화면에 있는 모든 위젯을 업데이트합니다.
위젯과 상태가 어떤 방식으로 업데이트되는지 알아보았으니 이제 연락처 UI를 만들고 메시지를 보낸 다음 업데이트되는 것을 지켜볼 수 있습니다. 그러려면 SociaLiteAppWidget
을 위젯 레이아웃에 있는 FavoriteContact
컴포저블로 업데이트해야 합니다. 이 레이아웃에서 No new messages
와 New Messages!
중 어느 쪽을 표시해야 하는지도 확인합니다.
com.google.android.samples.socialite.widget.ui
패키지의FavoriteContact.kt
파일을 검토합니다.
//Don't add this code.
@Composable
fun FavoriteContact(model: WidgetModel, onClick: Action) {
Column(
modifier = GlanceModifier.fillMaxSize().clickable(onClick)
.background(GlanceTheme.colors.widgetBackground).appWidgetBackground()
.padding(bottom = 8.dp),
verticalAlignment = Alignment.Vertical.Bottom,
horizontalAlignment = Alignment.Horizontal.CenterHorizontally,
) {
Image(
modifier = GlanceModifier.fillMaxWidth().wrapContentHeight().defaultWeight()
.cornerRadius(16.dp),
provider = ImageProvider(model.photo.toUri()),
contentScale = ContentScale.Crop,
contentDescription = model.displayName,
)
Column(
modifier = GlanceModifier.fillMaxWidth().wrapContentHeight().padding(top = 4.dp),
verticalAlignment = Alignment.Vertical.Bottom,
horizontalAlignment = Alignment.Horizontal.CenterHorizontally,
) {
Text(
text = model.displayName,
style = TextStyle(
fontWeight = FontWeight.Bold,
fontSize = 24.sp,
color = (GlanceTheme.colors.onSurface),
),
)
Text(
text = if (model.unreadMessages) "New Message!" else "No messages",
style = TextStyle(
fontWeight = FontWeight.Bold,
fontSize = 16.sp,
color = (GlanceTheme.colors.onSurface),
),
)
}
}
}
SociaLiteAppWidget
의Content
컴포저블에 있는Text("Hello World")
를FavoriteContact
컴포저블에 대한 호출로 바꿉니다.
이 컴포저블은 WidgetModel과 actionStartActivity
Glance 함수에 의해 생성된 작업을 받습니다.
- 이 호출을
when
블록에서 모델이WidgetModel
이 아닌 경우의ZeroState
앞에 추가합니다.
when (model) {
is WidgetModel -> FavoriteContact(model = model, onClick = actionStartActivity(
Intent(LocalContext.current.applicationContext, MainActivity::class.java)
.setAction(Intent.ACTION_VIEW)
.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
.setData("https://socialite.google.com/chat/${model.contactId}".toUri()))
)
else -> ZeroState(widgetId)
}
- Android 스튜디오가 다음 가져오기를 자동으로 추가하지 않는다면 수동으로 추가합니다.
import com.google.android.samples.socialite.widget.ui.FavoriteContact
import androidx.glance.appwidget.action.actionStartActivity
import android.content.Intent
import com.google.android.samples.socialite.MainActivity
import androidx.core.net.toUri
- 앱을 실행합니다.
- 즐겨찾는 연락처를 하나 선택하고 메시지를 보낸 다음 연락처가 답장하기 전에 앱을 종료합니다. 답장이 도착하면 위젯의 상태가 변경됩니다.
- 위젯을 클릭하여 채팅을 열고, 기본 화면으로 나가면 다시 상태가 업데이트되는 것을 봅니다.
6. 축하합니다
Codelab을 성공적으로 완료하고 Glance를 사용하여 위젯을 작성하는 방법을 알아보았습니다. 이제 여러 홈 화면에서 보기 좋게 표시되고 사용자 입력을 처리하며 스스로 업데이트되는 유려한 위젯을 만들 줄 알게 되었습니다.
main
브랜치에 있는 솔루션 코드를 가져오려면 다음 단계를 따르세요.
- SociaLite를 이미 다운로드한 경우에는 다음 명령어를 실행합니다.
git checkout main
- 다운로드하지 않은 경우에는 코드를 다시 다운로드하여
main
브랜치를 확인합니다.
git clone git@github.com:android/socialite.git