9
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

スマートフォン(Android)とスマートウォッチ(WearOS)でメッセージの送受信

Posted at

ノッチ付ディスプレイや画面内指紋認証、多眼カメラなど多種多様なスマートフォンが登場している一方で、スマートウォッチ市場は(AppleWatchを除いて)いまいち盛り上がりませんね。
とは言えスマートウォッチはそれなりの性能で様々なセンサを積んでいるので趣味や研究用途でいじるのにはおもしろいと思います。
自分も最近スマートウォッチ(WearOS)のアプリを作っていたのですが、スマートフォン(Android)との通信を実装する際のAndroidデベロッパーガイドの説明が少しわかりづらかったので補足してまとめておきます。

概要

スマートフォン(Android) -> スマートウォッチ(WearOS)でメッセージ送受信を行う際の実装を想定します。(逆の場合も実装は同様のはず)
スマートフォンとスマートウォッチにはそれぞれ以下を使いました。

  • Galaxy S9 (Android 9.0)
  • TicWatch E (WearOS 2.9)

以下実装についてですが、すでにWearOSアプリのプロジェクトが作成されている状態を前提とします。

送信側(スマートフォン)

準備

プロジェクトにスマートフォンアプリのモジュールを追加します。
File -> New -> New Module... -> Phone&Tablet Module を選択
スクリーンショット 2019-10-16 0.05.21.png
ここで注意点ですがパッケージ名が初期状態だと*com.hogehoge.[Module name]*のようになっているので、これをプロジェクト作成時に設定したものと同じにする必要があります。
理由としては、送信先ノード検出処理にてcontextを指定するため各ノードのアプリケーションIDが一致していなければならないからだと予想してます。
Androidデベロッパーガイドの手順には特に説明がないのですが、このあたりの作法は一般的なんですかね。
尚パッケージ名が違うと当然ですが検出されません。
(パッケージ名、アプリケーションID、Contextの関係はこの辺を参考に)

モジュール作成後build.gradle(Module)にWearOS APIの依存関係を追加 -> Sync

build.gradle

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.core:core-ktx:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'com.google.android.material:material:1.0.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    implementation 'com.google.android.gms:play-services-wearable:17.0.0' // <- https://developers.google.com/android/guides/setup を参考に
}

実装

ノードの識別子 WEAR_CAPABIRITY_NAME とメッセージの識別子 TO_WATCH_PATH を定義

MainActivity.kt
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        setSupportActionBar(toolbar)
    }
    /* 省略 */

    companion object {
        private const val WEAR_CAPABIRITY_NAME = "wear_capabirity"
        private const val TO_WATCH_PATH = "/hoge"
    }
}

送信先ノード検出処理とメッセージ送信処理を追加。今回はとりあえずすべてonCreate内に書きました。
フローティングアクションボタンを押すとメッセージを送信する感じで。
送信するデータはバイナリにする必要があります。

MainActivity.kt
class MainActivity : AppCompatActivity() {
    var nodeSet: MutableSet<Node>? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        setSupportActionBar(toolbar)

        // 送信先ノード検出
        val capabilityInfoTask: Task<CapabilityInfo> =
            Wearable.getCapabilityClient(applicationContext).getCapability(
                WEAR_CAPABIRITY_NAME,
                CapabilityClient.FILTER_REACHABLE
            )

        capabilityInfoTask.addOnCompleteListener(object : OnCompleteListener<CapabilityInfo> {
            override fun onComplete(task: Task<CapabilityInfo>) {
                if (task.isSuccessful) {
                    nodeSet = task.result?.nodes
                } else {
                    Toast.makeText(applicationContext, "送信先が見つかりません", Toast.LENGTH_SHORT).show()
                }
            }
        })

        //ボタンを押して送信
        fab.setOnClickListener { view ->
            nodeSet?.let {
                pickBestNodeId(it)?.let {
                    val dataText = "メッセージ"
                    val data = dataText.toByteArray(Charsets.UTF_8) //バイナリ変換
                    Wearable.getMessageClient(applicationContext)
                        .sendMessage(it, TO_WATCH_PATH, data)
                        .apply {
                            addOnSuccessListener {
                                Toast.makeText(
                                    applicationContext,
                                    dataText + " を送信",
                                    Toast.LENGTH_SHORT
                                ).show()
                            }
                            addOnFailureListener {
                                Toast.makeText(applicationContext, "送信失敗", Toast.LENGTH_SHORT)
                                    .show()
                            }
                        }
                }
            }
       }
    }

    //デベロッパーガイドに記載のものそのまま
    private fun pickBestNodeId(nodes: Set<Node>): String? {
        return nodes.firstOrNull { it.isNearby }?.id ?: nodes.firstOrNull()?.id
    }

    /* 省略 */

    companion object {
        private const val WEAR_CAPABIRITY_NAME = "wear_capabirity"
        private const val TO_WATCH_PATH = "/hoge"
    }
}

受信側(スマートウォッチ)

準備

WearOSアプリモジュールの res/values/wear.xml リソースを作成して以下のように記述。
item要素はスマートフォン側実装で定義した WEAR_CAPABIRITY_NAME に合わせます。

wear.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="android_wear_capabilities">
        <item>wear_capabirity</item>
    </string-array>
</resources>

実装

Activityに MessageClient.OnMessageReceivedListener を実装して Wearable.getMessageClient のリスナに指定します。

MainActivity.kt
class MainActivity() : WearableActivity(), MessageClient.OnMessageReceivedListener {
    override fun onMessageReceived(messageEvent: MessageEvent) {
        when(messageEvent.path) {
            FROM_PHONE_PATH -> {
                val data = messageEvent.data.toString(Charsets.UTF_8) //文字列に変換
                Log.d("debug", data) // -> D/debug: メッセージ
                /* 受け取ったデータを処理 */
            }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Enables Always-on
        setAmbientEnabled()

        Wearable.getMessageClient(applicationContext).addListener(this) //リスナに自身を指定
    }

    companion object {
        private const val FROM_PHONE_PATH = "/hoge"
    }
}

onMessageReceived ではスマートフォン側と同様に定義したメッセージの識別子(↑のFROM_PHONE_PATH)に応じてメッセージを処理します。
今回はとりあえずLogcatに出力するだけ

9
5
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
9
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?