0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Android】Glance APIを利用してアプリにウィジェットを追加する

0
Posted at

はじめに

ホーム画面ウィジェットは、アプリを開かなくても情報を確認できる有効なUIです。

この記事では、アプリにGlance API を用いてウィジェットを追加した際の実装方法を紹介します。

対象読者

  • Jetpack Composeを使ったAndroidアプリ開発経験がある方

  • アプリにウィジェットを追加したい方

対象とするアプリとウィジェットの目的

今回、ウィジェットを追加するアプリは私が個人で開発しているReadTrackというアプリです。
このアプリは積読の解消・読書週間の定着を目的としたアプリで、ユーザーは本の検索と登録、そして登録した本の読書記録の管理を行うことができます。
URL(GitHubリポジトリ)

URL(Play Store)

今回ウィジェットを追加する目的はユーザーにアプリの継続的な利用を促すことです。前述したとおり、このアプリはユーザーに読書週間の定着を目指すアプリであり、アプリを継続的にユーザーに利用してもらうことが重要になります。そのため、直近で登録した本の情報をウィジェットに入れて、ユーザーにアプリの起動を促すようにします。

Glance APIについて

Glance APIは、Androidアプリのホーム画面ウィジェットを実装するライブラリです。
ウィジェットを実装するためのライブラリとして従来のView開発ではRemoteViewsというライブラリが用いられていました。しかし、これには

  • XMLファイルを書く必要がある
  • 柔軟なUI表現や状態変化を扱いにくい
    などの問題が存在していました。

Glance APIはこれを解決し、

  • Kotlinのみで記述できる
  • 宣言的にUIを記述できる
    という特徴を持っています。

詳しくは以下のリンクをご覧ください。

実装の流れ

依存関係の追加

build.gradle(app)に以下の依存関係を追加します。

build.gradle(app)
dependencies{
    // For AppWidgets support
    implementation("androidx.glance:glance-appwidget:1.1.0")
    // For interop APIs with Material 3
    implementation("androidx.glance:glance-material3:1.1.0")
}

AppWidgetProviderInfo メタデータを追加する

ウィジェットの基本情報を定義します。android:initialLayout は、ウィジェット表示前に一時的に表示されるレイアウトです。

widget_info.xml
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="120dp"
    android:minHeight="60dp"
    android:initialLayout="@layout/glance_default_loading_layout"
    android:resizeMode="horizontal|vertical"
    android:widgetCategory="home_screen" />

GlanceAppWidgetクラスの拡張クラスを実装

GlanceAppWidgetクラスの拡張クラスを実装する際は、provideGlance関数をオーバーライドする必要があります。この関数はウィジェットのUIを定義する関数であり、ウィジェットのレンダリングに必要なデータを読み込むことができます。
ここで Jetpack Compose と同じ要領でUIを作成できるのGlance APIの利点です。

class MyAppWidget @Inject constructor(
    private val repository: DatabaseBooksRepository
) : GlanceAppWidget() {

    override suspend fun provideGlance(context: Context, id: GlanceId) {
        // Roomに保存している本の情報を取得
        val books = repository.getAllBooks()
        val recentBook = books.maxByOrNull { it.updatedDate }

        val bitmap: Bitmap? = recentBook?.let {
            withContext(Dispatchers.IO) {
                val url = URL(it.thumbnail)
                BitmapFactory.decodeStream(url.openStream())
            }
        }

        provideContent {
            Column(
                modifier = GlanceModifier
                    .fillMaxSize()
                    .padding(8.dp)
                    .background(Color.White),
                verticalAlignment = Alignment.CenterVertically,
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                if (recentBook != null && bitmap != null) {
                    Text(text = "最近更新された本")
                    Image(
                        provider = ImageProvider(bitmap),
                        contentDescription = "最近更新された本のサムネイル",
                        modifier = GlanceModifier.width(60.dp)
                            .height(90.dp)
                    )
                    Text(text = recentBook.title, maxLines = 1)
                } else {
                    Text("最近更新された本はありません")
                }

                Button(
                    text = "アプリを開く",
                    onClick = actionStartActivity<MainActivity>()
                )
            }
        }
    }
}

GlanceAppWidgetReceiverクラスの拡張クラスを実装

ウィジェットの追加や更新はBroadcastRecevier経由で行われます。Glanceでは専用のGlanceAppWidgetReceiverを継承します。

@AndroidEntryPoint
class MyAppWidgetReceiver : GlanceAppWidgetReceiver() {
    @Inject
    lateinit var repository: DatabaseBooksRepository

    override val glanceAppWidget: GlanceAppWidget by lazy {
        MyAppWidget(repository)
    }
}

マニフェストで AppWidget を宣言する

追加したGlanceAppWidgetReceiverをマニフェストで宣言し、Androidシステム側で見えるようにします。

AndroidManifest.xml
<receiver android:name=".glance.MyAppWidgetReceiver"
    android:exported="true">
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
    </intent-filter>
    <meta-data
        android:name="android.appwidget.provider"
        android:resource="@xml/my_app_widget_info" />
</receiver>

実装結果

Project Logo

まとめ

個人開発アプリにウィジェットを追加することで、UX向上を実現できました。
ウィジェットは制約が多い一方で、設計を割り切ることでユーザー体験を向上させる有効な手段だと感じました。

おわりに

Glance APIはまだ情報が少ないため、これからウィジェット実装を検討している方の参考になれば幸いです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?