見出し画像

Now in REALITY Tech #59 WearOSでREALITYのアプリを作ってみる

こんにちは!REALITY Androidチームのぴかです!

最近、REALITYで行われた開発合宿の賞品として、去年の10月に販売されたGoogleのスマートウォッチを頂きました!!
せっかくなので、REALITYに役立ちそうな何かしらのアプリを作ってみようと思います。

Androidのスマートウォッチ開発について

Androidのスマートウォッチでは基本的にGoogleが開発しているWearOSが利用されており、こちらに対応するアプリを開発することでスマートウォッチ上でアプリを利用することができます。

開発の準備

WearOSアプリはAndroidアプリと同様にAndroid Studio上で開発が可能です。

New ProjectからWear OSを選択

実機確認もできますが、エミュレーター上で起動も簡単です。
(むしろ、スマートウォッチはケーブルを指すことができないので個人的には実機確認が少し面倒でしたw)

エミュレーターのHome
エミュレーターでのアプリ起動
実機での確認(レイアウトがちょっとズレてる)

スマートウォッチの機能について

WearOSはAndroidアプリと比較するとできないことが多くあります。
例えば、REALITYアプリのようにアバターを表示したりは現実的ではないですし、そもそもの画面が小さいのでいろんな画面を遷移するような動作は向いていません。
また、入力に関しても狭い文字盤で入力するのは難しいですし、音声入力も面倒です。

逆に、通知の受信などスマホを見ていない場合などの手軽に何かを確認することにはとても向いています。
また、使い方は難しいですが、心拍数などのセンサー等も利用できるのでそういった使い方もできます。

これらのことを考えると、REALITYでは通知、チャット、コメントあたりの補助機能としての役割が適切と思い、今回は視聴中の配信のコメントを確認できるWearOSのアプリを作ってみました。
(注: 実験的な開発なので現状でこのアプリを公開する予定はありません。)

WearOSのUI

wearOSではJetpack Composeが使えるのでこちらを使ってUIを作っていきます。

WearOSのComposeでは、スマートフォンアプリで普段使っているようなComponentに加え、WearOS用に用意されているComponentも使用することができます。

ボタンやテキスト、通知っぽいようなカード

ScalingLazyColumnは通常のLazyColumnに加えて、画面端の場合にはサイズを小さくしてくれます。

ScalingLazyColumn

WearOSのデータ通信

WearOSは通信を行わずWearOSのみで完結するアプリを開発することもできますが、REALITYのようにメインのコンテンツが通信が必要とするものの場合には、スマートフォンを通したりして通信を行う必要があります。

スマートウォッチの通信 

REALITYのコンテンツは大半がユーザー認証が必要であり、今回の開発でWatch側にユーザー認証の機能を盛り込むのはとても大変なので、Bluethoth経由でスマートフォンからデータを送受信する形式を取ります。

スマートフォンとスマートウォッチの通信手段は MessageClient や DataClient などのデータレイヤAPIを利用して実行します。

実装

スマートフォン側

まず、データレイヤAPI利用のために build.gradle に以下の依存を追加します。

implementation ("com.google.android.gms:play-services-wearable:[version]")

また、今回は通信に関しては DataClient を利用して通信を行います。
DataClientは、コンポーネントによるDataItemやAssetに対して書き込みや読み込みを提供し、DataItemをWearOSとスマートフォン間で同期することでスマートフォンのデータをWearOS側に提供します。

ということで、REALITYの配信コメントを取得している実装の途中でいい感じに呼び出してみます。

fun sendComments(dataClient: DataClient, commentList: List<String>) {
    val dataMap = PutDataMapRequest.create(dataLayerPath)
    dataMap.dataMap.putStringArray("message", commentList.toTypedArray())
    val request = dataMap.asPutDataRequest()
    val result = dataClient.putDataItem(request)
    result.addOnSuccessListener { dataItem -> Log.d("WATCH_OS_LOG", "Sending successful: $dataItem") }
        .addOnFailureListener { e -> Log.e("WATCH_OS_LOG", "Sending failed: $e") }
}

DataClientに送信するDataMap(DataItem)にスマートウォッチと共有するPathと送信内容を詰めながら、書き込みを行います。

スマートウォッチ側

WearOS側ではスマートフォン側で書き込まれたDataItemを取得し、それをUI上で表示します。

class MainActivity : ComponentActivity(), DataClient.OnDataChangedListener {
    val dataPath = "/reality_data_path"
    val viewModel = MainViewModel()

    override fun onResume() {
        super.onResume()
        Wearable.getDataClient(this).addListener(this)
    }

    override fun onPause() {
        super.onPause()
        Wearable.getDataClient(this).removeListener(this)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            WearApp(viewModel.commentList)
        }
    }

    override fun onDataChanged(dataEvents: DataEventBuffer) {
        for (event in dataEvents) {
            if (event.type == DataEvent.TYPE_CHANGED) {
                val path = event.dataItem.uri.path
                if (dataPath == path) {
                    val dataMapItem = DataMapItem.fromDataItem(event.dataItem)
                    val messages = dataMapItem.dataMap.getStringArray("message")
                    messages.forEach { message ->
                        val commentData = Json.decodeFromString<CommentData>(message)
                        viewModel.addReceivedComment(commentData)
                    }
                }
            }
        }
    }
}

ActivityのライフサイクルでDataClientの管理を行いながら、OnDataChangedListenerを継承することで、DataItemのListenerであるonDataChangedをoverrideし、スマートフォン側からのDataItemの変更を待機します。

完成

ということで、上記のような形で配信へ入室すると配信のコメントを読み取れるアプリを試作してみました!

まずはエミュレーターで確認してみると…

アプリ起動時
配信に入るとコメントが読み込まれる
最新コメントまで追いついて
追加されたコメントも読み込める👏
実機の確認も大丈夫そう!

詰まった点

実装事例が少ない

そもそもAndroidのスマートウォッチがそこまで普及率が高くなさそうなこともあり、現状ではWearOSの開発はそこまで盛んではなさそうです。
公式のリファレンスはとても丁寧なのですが、実際のコードに落とし込むのには少し苦労がありました…

applicationId が対応アプリと同一にすること

スタンドアロンのアプリとして、スマートフォンのアプリと通信しないアプリを作成する場合は必要ありませんが、今回のアプリのように通信を行う場合には同じapplicationIdを利用する必要があります。
他のアプリと通信するデータを区別するために必要なのはわかるんですが、ここの記述を見逃しており、なかなか通信が成功せず時間がかかっていました…

Wear OS アプリとスマートフォン アプリで同じアプリ ID を使用する必要があります。

Wear でメッセージを送受信する

署名が対応アプリと同一にすること

applicationIdの件にも近いですが、署名についても対応アプリと同一である必要があります。

Wear アプリに付属のモバイルアプリがある場合は、Data Layer API を使用して通信するために、2 つのアプリそれぞれに同じキーを使用して署名する必要があります。

Wear アプリをパッケージ化して配信する

applicationIdと同様ではあるんですが、エミュレーターの場合には署名に関してはすり抜けてくれるらしく、通信ができていたので、実機確認のタイミングで通信がなかなかできず、詰まっていました…
アプリも配信予定がなかったので、ここの記述自体も見逃していたので発見までが大変でしたw

まとめ

現状、REALITYではWearOSのアプリを作って公開する予定はありませんが、配信の仕方によってはコメントを見れるようなアプリは役に立つのかも?と思い、試作アプリを作ってみました!
普段やらない開発だったので、詰まる点も多かったですが、実機で動いているのが見れたのはやっぱり楽しいですね!
他にも面白かったり、役立ちそうな使い方が見つかったら開発を頑張るかも?…

REALITYでは、WearOSや新しい技術開発に興味がある仲間を募集中です!

Android開発以外にも色々なポジションで募集も行っていますので、ご興味を持っていただけたら覗いていただけると嬉しいです!