1. 始める前に
この Codelab では、SociaLite のアプリ ウィジェットの作成プロセスを説明します。まず、シンプルな Glance ウィジェットを作成し、これを SociaLite とホーム画面に追加します。次に、Glance のコンポーネントとテーマを使用してウィジェットにゼロ状態を追加します。その後、ユーザーがウィジェットからお気に入りの連絡先を選択できるようにします。最後に、アプリからウィジェットを更新する方法を説明します。
前提条件
- Kotlin の基礎知識があること。
- Codelab Android Studio をセットアップするを完了していること、または Android Studio の使用方法と Android 15 のエミュレータまたは実機でアプリをテストする方法に精通していること。
- Hilt の基礎知識があること。
- Compose の基礎知識があること。Glance では Jetpack Compose のコンポーズ可能な関数は使用しませんが、フレームワークとコーディング スタイルは流用します。
この Codelab で学ぶこと
- ウィジェットをサポートするようにアプリを構成する方法。
- Glance コンポーネントを使用してレスポンシブ レイアウトを構築する方法。
GlanceTheme
を使用してユーザーのホーム画面でダイナミック カラーをサポートする方法。- ウィジェットでユーザー操作を処理する方法。
- アプリからウィジェットを更新する方法。
必要なもの
- Android Studio の最新バージョン
- Android 12 以降を実行できるテストデバイスまたはエミュレータ。
- Android 12 以降の SDK。
2. セットアップする
スターター コードを取得する
- 「Android 15 でのエッジ ツー エッジの適用の処理」または「予測型『戻る』アニメーションを追加する」の 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 Studio で SociaLite を開き、Android 15 を搭載したデバイスまたはエミュレータでアプリを実行します。次のような画面が表示されます。
ジェスチャー ナビゲーションを追加した SociaLite
3. ウィジェットを追加する
ウィジェットとは
ウィジェットとは、他の Android アプリ内に埋め込み可能なアプリの一部分のことです。最も一般的な例は、ユーザーのホーム画面です。
アプリにウィジェットを追加することで、ユーザーは一般的なタスクをすばやく開始したり、情報をひと目で確認したりできるほか、アプリが提供するコンテンツでデバイスをカスタマイズできるようになります。
Glance とは
Jetpack Glance とは、Kotlin で Compose のような API を使用してウィジェットを作成するためのライブラリです。再コンポーズ、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 を追加する
スターター コードにより、Glance の各バージョンとライブラリ座標が SociaLite のバージョン カタログ libs.versions.toml
に追加されています。
libs.versions.toml
[versions]
//..
glance = "1.1.1"
[libraries]
glance-appwidget = { group = "androidx.glance", name = "glance-appwidget", version.ref = "glance" }
glance-material = { group = "androidx.glance", name = "glance-material3", version.ref = "glance" }
また、Glance の依存関係は SociaLite の app/build.gradle.kts
ファイルに含まれています。
build.gradle.kts
dependencies {
...
implementation(libs.glance.appwidget)
implementation(libs.glance.material)
...
}
- これらのファイルを変更した場合は、プロジェクトを同期して Glance ライブラリをダウンロードします。
GlanceAppWidget
と GlanceAppWidgetReceiver
を作成する
Android はブロードキャスト レシーバを使用して、ウィジェットが追加または削除されたことや、ウィジェットを更新する必要があることを SociaLite に通知します。Glance は、AppWidgetProvider を拡張する抽象レシーバクラス GlanceAppWidgetReceiver を提供します。
GlanceAppWidgetReceiver
の実装は GlanceAppWidget
のインスタンスも提供します。このクラスは Glance のコンポーズ可能な関数をリモートビューにレンダリングします。
スターター コードには、GlanceAppWidget
を拡張する SocialiteAppWidget
と、GlanceAppWidgetReceiver
を拡張する SocialiteAppWidgetReceiver
という 2 つのクラスが含まれています。
まず、次の手順を行います。
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 を構成する準備が整いました。
アプリ ウィジェットのプロバイダ情報を追加する
- **
res/xml
** を右クリックし、[New] > [XML resource file] を選択します。 - ファイル名として
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
ファイルを更新してウィジェットをテストする準備が整いました。次に、ファイル内で receiver
要素を application
要素の子として定義します。このレシーバは 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>
- アプリをコンパイルして実行します。
- アプリが起動したら、ホーム画面にウィジェットを追加します。たとえば Google Pixel では、背景を長押しし、[ウィジェット] > [SociaLite] を選択すると、ホーム画面にウィジェットを追加できます。
ウィジェットには「Hello World」というメッセージが表示されており、背景は透明です。このままでは見栄えが悪く、機能性にも劣るため、次のセクションでより複雑なレイアウトを追加し、マテリアル デザインのカラーを使用してウィジェットの外観を改善します。
4. デザインを改善する
このウィジェットは静的で、優れたウィジェットに備わっている多くの機能が欠けています。優れたウィジェットには次のような特長があります。
- 常に新しいコンテンツを簡潔に表示し、機能のシンプルさを維持する。
- サイズ変更可能なレイアウトにより、なるべく隙間ができないようにする。
- アプリ ウィジェット ホストの背景色に合わせる。
優れたウィジェットの特長について詳しくは、ウィジェットをご覧ください。
Scaffold を追加する
次に、Glance の Scaffold
コンポーネントで表示するようウィジェットを更新します。
Scaffold
は Glance ライブラリによって提供されます。TitleBar
でウィジェットの UI を表示するためのシンプルなスロット API です。背景色を GlanceTheme.colors.widgetBackground
に設定し、パディングを適用します。これが最上位のコンポーネントになります。
まず、次の手順を行います。
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")
}
}
}
- 更新を確認するには、アプリを再実行してから、ウィジェットの新しいコピーをホーム画面に追加します。
ウィジェットは外部ホストによって表示されるリモートビューであることに注意してください。後ほど、アプリ内でウィジェットを自動的に更新する機能を追加します。それまでは、ウィジェット選択ツールからウィジェットを追加してコードの変更を確認する必要があります。
ご覧のようにかなり改善されましたが、他のウィジェットと比べて色に違和感があります。ホーム画面では、ウィジェットの色はユーザーのテーマ設定に基づいて設定されることが期待されるためです。ダイナミック カラートークンを使用することで、ウィジェットのテーマをデバイスの壁紙とテーマに適合させることができます。
ダイナミック カラーを追加する
widgetBackground
のカラートークンを Scaffold の背景に追加し、onSurface
のカラートークンを TitleBar
のテキストおよび Text コンポーネントに追加します。テキストのスタイルを更新するには、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))
}
- 更新を確認するには、アプリを再実行してから、ウィジェットの新しいコピーをホーム画面に追加します。
これで、ウィジェットの色がホーム画面の他のウィジェットのテーマに合わせて変更されるようになります。ユーザーが背景を変更したり、ダークモードに設定したりすると、色が自動的に更新されます。カラフルな背景の場合、ウィジェットの色は配置されている背景のセクションに合わせて変更されます。
|
|
ゼロ状態を追加する
次に、状態とウィジェットの構成について考える必要があります。ホーム画面にウィジェットが追加されたときに構成を求める場合、通常はゼロ状態を表示することをおすすめします。ゼロ状態では、ユーザーにウィジェットを構成するよう促します。ウィジェットに構成アクティビティを追加し、ゼロ状態から構成アクティビティへのリンクを設定します。
この 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
ゼロ状態
コンポーズ可能な関数 Content
で WidgetModelRepository
からウィジェットのモデルを読み込み、使用できるモデルがない場合はゼロ状態を表示し、それ以外の場合はウィジェットの通常のコンテンツを表示します。現時点では「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 Studio で次のインポートが自動的に追加されない場合は、手動で追加します。
import androidx.compose.runtime.collectAsState
また、Content
にウィジェット ID とリポジトリを渡すように provideGlance
を更新する必要があります。
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 Studio で
com.google.android.samples.socialite.widget.ui
パッケージが自動的にインポートされなかった場合は、SociaLiteAppWidget
のインポート セクションに次のコードを追加します。
import com.google.android.samples.socialite.widget.ui.ZeroState
- 更新を確認するには、アプリを再実行してから、ウィジェットの新しいコピーをホーム画面に追加します。ウィジェットに ZeroState コンポーネントとボタンが表示されます。ボタンをクリックすると構成アクティビティが開きます。次のセクションで、このアクティビティからウィジェットの状態を更新します。
構成アクティビティ
コンポーズ可能な関数 ZeroState を確認します。この関数は、ZeroState.kt
ファイルのパッケージ com.google.android.samples.socialite.widget.ui
にあります。
@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
には SociaLite のメイン アクティビティを開く clickable
修飾子があります。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 Studio で自動的にインポートされない場合は、以下を追加します。
import com.google.android.samples.socialite.widget.model.WidgetModel
import androidx.glance.appwidget.updateAll
import kotlinx.coroutines.launch
このコードブロックはウィジェットの状態を更新します。まず、リポジトリを使用して WidgetModel
を保存するか、選択した連絡先の情報で更新します。次に、updateAll
suspend 関数を呼び出します。これはホーム画面上のすべてのウィジェットを更新する関数で、アプリの任意の場所から呼び出すことができます。このブロックは最後に、ウィジェットが正常に更新されたことを示すため、構成アクティビティの結果を設定します。
- ホーム画面でウィジェットを実行して置き換えると、新しいゼロ状態が表示されます。
- [Select favorite contact] をクリックすると、構成アクティビティに移動します。
- 連絡先を選択すると、ウィジェットが更新されます。ウィジェットにはまだお気に入りの連絡先が表示されませんが、この機能は次のセクションで追加します。
ウィジェットのデータを管理する
- [App Inspection] ツールを開き、必要に応じてプロセスに接続し、[Database Inspector] タブを選択してアプリのデータベースのコンテンツを確認します。
- ウィジェットでお気に入りの連絡先を選択し、「Hello World」に更新されることを確認します。App Inspection ツールに戻ると、[WidgetModel] タブにウィジェットのエントリが 1 件表示されているはずです。場合によっては、表を更新するか、[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 を追加し、新しいメッセージが届いたら更新する
この Codelab の最後のセクションでは、ウィジェットに連絡先の UI の最終版を実装し、その連絡先からの未読メッセージがある場合に UI を更新します。
model
パッケージの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)
}
}
}
このメソッドには 2 つのパラメータがあります。contactId
は更新される連絡先の ID で、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
の呼び出しに置き換えます。
このコンポーズ可能な関数は、Glance 関数 actionStartActivity
によって作成された WidgetModel と Action を受け取ります。
- モデルが
WidgetModel
でない場合は、ZeroState
の前にwhen
ブロックの呼び出しを追加します。
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 Studio で以下のインポートが自動的に追加されない場合は、ここでインポートします。
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
- SocialLite をまだダウンロードしていない場合は、
main
ブランチを表示するためもう一度コードをダウンロードします。
git clone git@github.com:android/socialite.git