Android Dev Summit 2021で発表されたAppWidgetの進化まとめ

こんにちは。KyashのMobileチームに所属しているAndroidエンジニアのどすこいです。
10月下旬にAndroid Dev Summit 2021が開催され、様々なアップデートが公開されましたね。

そこでAppWidgetに関するとても大きなアップデートがありました。今回はそのアップデートに関して一部抜粋して深掘りしていきたいと思います。
※このブログが公開された時点ではAPIが公開されているわけではないので、今後実装方法が変更される可能性があります。ご了承ください。

AppWidgetとは何か

まずはAppWidgetの説明からです。
Android Dev Summitの動画では、アプリを起動しなくてもアプリの機能が一目見てわかるもの、と紹介されています。
公式の方には次のような記述もあり、アプリを開かずにアプリの機能を提供できるものと考えてよいでしょう。

​​アプリ ウィジェットとは、他のアプリ(ホーム画面など)に埋め込んで定期的に更新を取得することができる小さなアプリビューです。

Widgetには以下のような特徴があります。

  • AppWidgetHostのプロセス上にある
  • Widgetでできること、表示できることには一定の制限がある

AppWidgetはどのように動くのか

Widgetがどのように振る舞うかを定義したAppWidgetProviderや、metadataを定義したAppWidgetProviderInfoをAndroidManifestに登録すると、OSはWidgetのプレビューを表示できます。
AppWidgetProviderはRemoteViewとWidgetのレイアウトが定義されたxmlを使います。AppWidgetには使えるViewの制限があります*1

そしてAppWidgetProviderはAppWidgetManagerを通じてWidgetを更新します。

AndroidManifestにはBroadcastReceiverとしてAppWidgetProviderを定義します。OSがブロードキャストイベントを通じて、定期的にWidgetを更新します。

AppWidgetの歴史

AppWidgetのAPIは2008年から提供され、今に至るまで大きく変わってきませんでした。実際、2012年から2021年ではAppWidgetに関するアップデートは1つしかありません。しかしAndroid12では大きなアップデートがあります。

AppWidgetの改善

Rounded corners

今までのAppWidgetはデフォルトでは角が角ばっているものでした。Android12では多くのUI要素が角丸になり、AppWidgetsが他のウィジェットやシステムの外観と一貫して見えるようになりました。

角丸を表現するパラメータが2つ追加されました。
1つ目はAppWidgetの背景の角丸を表す @android:dimen/system_app_widget_background_radius です。

<shape xmlns:android="http://schemas.android.com/apk/res/android">
   <corners android:radius="@android:dimen/system_app_widget_background_radius"/>
</shape>

2つ目は似たようなパラメータで、AppWidgetの中のViewの角丸を表す @android:dimen/system_app_widget_inner_radius です。

<shape xmlns:android="http://schemas.android.com/apk/res/android">
   <corners android:radius="@android:dimen/system_app_widget_inner_radius"/>
</shape>

f:id:dosukoi_blog:20211118172326p:plain
Android Developersから引用

Responsive Layout

Responsive Layoutを実現するために新たなAPIが追加されました。

まずはコードを見てみましょう。

SizeF(180f, 110f) to RemoteViews(
   context.packageName,
   R.layout.widget_small
)
SizeF(270f, 110f) to RemoteViews(
   context.packageName,
   R.layout.widget_medium
)
SizeF(270f, 280f) to RemoteViews(
   context.packageName,
   R.layout.widget_large
)

例えば縦幅が110dp固定の時、Widgetの横幅が180dp以上269dp以下の場合はR.layout.widget_smallのレイアウトがWidgetに適用され、それ以上の場合は270dpをサポートしているR.layout.widget_mediumが適用されます。

同様に横幅が180dp固定の時、Widgetの縦幅が110dp以上279dp以下の場合はR.layout.widget_smallのレイアウトがWidgetに適用され、それ以上の場合は280dpをサポートしているR.layout.widget_largeが適用されます。

f:id:dosukoi_blog:20211118172607p:plain
Android Dev Summit 2021から引用

横幅が270dp固定の時も同じように、Widgetの縦幅が110dp以上279dp以下の場合はWidgetにR.layout.widget_mediumのレイアウトが適用され、それ以上の場合は280dpをサポートしているR.layout.widget_largeが適用されるという仕組みです。

f:id:dosukoi_blog:20211118172854p:plain
Android Dev Summit 2021から引用

Sizing

サイズを設定するための新しいattributeが追加されました。
今まではminWidth, minHeightでの指定しかできず、端末の横幅や縦幅によって使うセルの数が違ってきましたが、Android12から使うセルの数を指定できるため端末によっての差異がなくなるだろうと考えられます。

f:id:dosukoi_blog:20211118172942p:plain
Android Dev Summit 2021から引用

ComposeによるWidgetの作成

今回のアップデートの目玉はこれでしょう。
今まではRemoteViewの制約により、Widget内で使えるViewの種類には限りがありました。この制約によりWidget作成に苦戦した方も少なくないかと思います。
ですが、Android12からはComposeによるWidget作成が可能になります。

Glanceとは

今回新たに追加されたAPIにGlanceというものがあります。

f:id:dosukoi_blog:20211118173046p:plain
Android Dev Summit 2021から引用

Glanceは、Compose Runtimeを経由して従来のRemoteViewに置き換えてくれる仕組みです。
これにより開発者はRemoteViewの制限に苦しめられることなく、ComposeでUIを表現できるようになります。

Creating a Glance AppWidget

今までのAppWidgetと同じようにAndroidManifestにGlanceAppWidgetProviderを宣言を忘れないようにしましょう。

f:id:dosukoi_blog:20211118173119p:plain
Android Dev Summit 2021から引用

次にこちらのGlanceAppWidgetを継承したクラスのContent関数内でComposeを書いていきます。
Content関数自体がComposableな関数になっているので、開発者はこの関数内でComposeを書けるようになります。

f:id:dosukoi_blog:20211118173135p:plain
Android Dev Summit 2021から引用

GlanceAppWidget内で使えるComposeは従来のComposeとよく似ていますが、AppWidgetに対応して少し違う箇所があります。

Column(
    modifier = Modifier.expandHeight().expandWidth()
) { ... }

親Viewいっぱいまで広げたい時、ComposeであればfillMaxHeight()やfillMaxWidth()を使いますが、上のコードにもある通り、GlanceAppWidgetではexpandHeight()やexpandWidth()を使っています。

まだAPIが公開されていないので、どれほど従来のComposeと違いがあるかはわかりませんが、実際に導入する時には公式のドキュメントや実装を読んで使っていく必要がありそうです。

またなぜ従来のComposeと違った関数名なのか深掘りするのも面白そうですね。

Handle user interaction

ユーザーの操作のハンドリングも以前よりも相当楽になりました。
今まではButtonが押下された時、Activityに飛ばすPendingIntentを発行して、RemoteViewのあるidに対して

view.setOnClickPendingIntent(R.id.button, pendingIntent)

などとしなくてはいけませんでしたが、今回のアップデートでは

Modifier.clickable(launchActivity<HogeActivity>(..))

これだけで良くなりました。 非常に簡潔ですね。

WorkManagerの起動などのカスタムアクションの実行も簡単になりました。

Modifier.clickable(customAction<HogeAction>(..))

これだけです。

また、従来のAppWidgetProviderには、onUpdateでWorkManagerを起動すると、onUpdateの無限ループに入ってしまうというバグがありました。(Googleの人曰く、バグというよりも仕様)

ですが、今回のアップデートでネットワーク通信をする場合はWorkManagerを使いましょうという発表があったため、上記の無限ループは回避できるようになったことを期待しています。

Responsive Layout for Compose

ComposeによるResponsive Layoutのオプションも3つ提供されています。

  • Single
  • Exact
  • Reponsive

1つずつ紹介していきます。

Single

Singleはサイズ変更自体は可能なWidgetですが、UIの更新はされずに単にスケール処理されます。
Singleがデフォルトのオプションです。

f:id:dosukoi_blog:20211118173404p:plain
Android Dev Summit 2021から引用

Exact

ユーザーがWidgetのサイズを変更し、その度にUIの表示を変更したいときはExactを使いましょう。

f:id:dosukoi_blog:20211118173422p:plain
Android Dev Summit 2021から引用

Responsive

ユーザーがWidgetのサイズを変更し、その度に表示するUIそのものを変えたい場合(smallの時はリストを出さず、mediumからリストを表示するなど)は、Reponsiveを使うことをおすすめします。 DpSize形式で、わかりやすいように特定の形状のWidgetのサイズをマッピングしています。

f:id:dosukoi_blog:20211118173505p:plain
Android Dev Summit 2021から引用

最後に

Android Dev Summit 2021はとてもワクワクする内容ばかりでしたね!
Room2.4のAuto Migrationも気になりますし、foldable端末の対応も各企業どう行なっていくかも楽しみにしています。

その中でも今までほとんど大きな変化のなかったAppWidgetの内容がここまでアップデートされたのは個人的にものすごくびっくりしました。APIが公開されたらいち早く触ってみたいですね。


Kyashでは一緒に働くAndroidエンジニアを募集しています。
最新の技術やバージョン更新に気を配りつつプロダクトへの導入を積極的に行っています。
流行りのJetpack Composeだけでなく、アクセシビリティ対応であったり、当人のやりたいことを実現できる環境だと感じています。
興味がありましたら、ぜひ下記のリンクからカジュアルにお話ししましょう!

open.talentio.com

docs.google.com