Android Wear2.0 Watch face Complicationsを作る 基礎編

2016/10/3 1434hit

Android Wear2.0 Watch face Complicationsを作る

はじめに

この記事はAndroid Wear2.0 Preview3を元にかかれています。実際のリリース版ではAPIの挙動が変わる可能性があります。
対象読者はAndroidスマートフォンアプリの経験者で端末にOSイメージの焼き込みを行ったことがある人です。

目次

基礎
DataSync
多データ型対応


Watch face Complicationsとは

Android WearではWatch faceという文字盤アプリを開発することができ、好きなデザインや天気予報が表示される文字盤など多様な文字盤が多くリリースされています。
関連記事:Android WearのWatch faceを作る(概念編)
しかしながら、画面にデータを表示する処理はWatch faceを開発する開発者がデータを取得して表示するまで全てを作る必要があったため、Watch faceの開発者は負担が大きかった上に必ずしも好きなデザインのWatch faceに欲しいデータが表示されるとは限りませんでした。

Android Wear2.0で追加される予定のWatch face ComplicationsはWatch face上に別アプリのデータを表示するための仕組みです。
Watch faceとデータを表示したいアプリ(以降Data providerと記す)がComplicationsAPIにより協調的に動作することが出来るようになります。

これによりユーザーが選んだ任意のWatch faceでユーザーが選んだ任意のData providerのデータを表示することができるようになります。


また、複数のアプリが並んで表示されるNotificationと違いWatch face ComplicationsはWatch faceの特定の領域に一つのアプリのデータが常駐する形になるため、ユーザーにとってもいつも同じ場所に同じ情報が表示され、ひとめ見るだけの視認性アップを期待できます。

ホームスクリーンにデータを表示するのはAndroidスマートフォンのホームウィジェットに近いですね。
また、送信元と送信先がお互いを意識しない状態でデータをやり取りして協調動作する仕組みは暗黙的Intentに似ている感じがします。

Watch face Complicationsの仕組み

Watch faceがOSにデータの要求を行うとOSがData providerにデータの要求を行い、Data providerがOSが定めた形式のデータを送り、OSがWatch faceにデータを送りWatch faceがWatch face Complicationを描画するという仕組みになっています。
Complication APIの使い勝手はホームウィジェットに似ていますが、開発者視点で見るとその仕組みは若干異なります。
最大の特徴は描画処理をData providerが一切行わず、Watch faceが行う点です。Watch faceはデータをData providerからもらいながらも実際にどのように描画するかについては自由に行うことができます。
これによりWatch faceは不特定なアプリのデータを表示しながら世界観を保つことができます。

今回はData providerを使用して2.0に対応しているWatch faceにデータを表示するアプリを作ってみます。

Android Wear2.0 Preview3の使い方


Android Wear2.0 Preview3を使用するには以下の準備が必要です。
この工程では不安定なバージョンのOSを使用するため端末が起動しなくなるなどの不測の事態が発生する可能性があります。自己責任でご利用ください。

スマートフォンの準備

ベータ版Android Wearアプリ
Android Wear2.0 Preview3では新たにGoogle Play Storeにスマートウォッチから直接アクセスできるようになったため、スマートフォンアプリ側からアカウントデータを共有する機能がついた、ベータ版のスマートウォッチアプリを使用しなくてはいけなくなりました。

ベータ版を使用するにはまず、Android Wear developer preview開発者用のGoogleGroupsに参加します。

次にテスターへの申し込みを行います。
申請ページでテスターになるをクリックします。
数分から1時間ほどでベータ版のAndroid WearアプリがGoogle Playでダウンロードできるようになります。

スマートウォッチの準備

Wear2.0 Preview3を実機で試すにはプレビュー版のダウンロードサイトより、イメージをダウンロードしadbを使ってスマートウォッチに転送する必要があります。
現在のところ
lg watch urbane 2nd edition


Huawei Watch向けにイメージが用意されています。


Huawei WatchについてはAmazon.comが日本への発送も対応していて送料を含めても日本より安くなっています。

急ぎでないならこちらを使うという手もあります。

2.0の焼き方

Wearに2.0のイメージを焼きます。
イメージの焼き方については一般的なスマートフォンと同じ為省略します。

開発者プレビューを有効化

実機を使って開発を行う場合、イメージを焼いてスマートフォンとペアリングした後にWear側も開発者向けオプションを有効にする必要があります。

画面を上部から下に向かってスワイプしギアのアイコンをタップし設定画面を開きます。

システムー概要を開きビルド番号を7回タップします。

設定まで戻り、開発者向けオプションを選び、ADBデバッグを有効にします。
PCとWearがUSBで接続されている場合はWearにPCとのUSBデバッグを許可するかどうかを選ぶ画面が出てくるため「このパソコンからのUSBデバッグを常に許可する」を選択します。
Bluetooth経由でテストを行う方法についてはWatch faceを作る 5分で作るWatch faceを参照してください。

実機を使わない手軽な方法としてはエミュレータを使用するという方法もあります。
エミュレータの場合はWearを選びAPIレベル24を選択するとWear2.0となります。


新規プロジェクトの作成

それでは今回はランダムな値を表示するData providerを作ってみます。
Android Studioで新規プロジェクトを作ります。

Application nameなどは任意の値を設定

Android Target DevicesにPhone and TabletとWearを選び、Phone and Tabletのminimum SDKはAPI18以上、Wearは24以上を選択します。
今回はスマートフォン向けのアプリは作りませんが、次回以降で使用するためPhone and Tabletも選んでおきます。

Add an Activity to MobileではEmpty Activityを選択します。


Customize the ActivityはActivity NameにMainActivityを設定します。

Add an Activity to WearではAdd No Activityを選択してFinishを選択します。


2つのモジュールが生成されますが、今回はWearモジュールだけを作ります。

Complication Provider Serviceの作成

Data providerはComplicationProviderServiceを継承したクラスでデータの提供を行います。

モジュールWearにSampleComplicationProviderServiceクラスを作成しComplicationProviderServiceを継承します。
ComplicationProviderServiceクラスは抽象メソッドonComplicationUpdate()を持っているのでOverrideします。
SampleComplicationProviderService.java

public class SampleComplicationProviderService extends ComplicationProviderService {

@Override
public void onComplicationUpdate(int i, int i1, ComplicationManager complicationManager) {

}
}

このメソッドはWatch faceが新しいデータを要求しているときに呼ばれます。

引数名が意味を持たない名前のため何が渡ってくるかわかりにくいですが、第一引数には画面に表示するWatch face Complicationごとに振られたidが渡されます。
Watch faceによっては複数のComplicationを同時に表示することが出来るため、このidを使ってそれぞれのWatch face Complicationを区別して、たとえば世界中の時計を表示するData providerの場合、IDが1だった場合は日本、IDが2だった場合はサンフランシスコの時間を返すといったように区別して値を返すことができます。
第二引数にはどのような形式のデータ(後述)が求められているかが指定されるtypeが渡されます。
わかりやすくするために引数の名前を変更しておきましょう。
SampleComplicationProviderService.java

public class SampleComplicationProviderService extends ComplicationProviderService {

@Override
public void onComplicationUpdate(int complicationId, int type, ComplicationManager complicationManager) {

}
}


データを返す

ここでは0〜99までのランダムな値を返すWatch face Complicationを作ってみます。
まずは0〜99の乱数を生成します。
SampleComplicationProviderService.java

@Override
public void onComplicationUpdate(int complicationId, int type, ComplicationManager complicationManager) {
int value = (int)(Math.random()*100);
}


次にデータのタイプを調べて値を返します。
どのようなデータタイプをサポートしているかはWatch faceにより変わるため、複数のデータタイプに対応することでより幅広いWatch faceで利用が可能になります。
Watch face側が対応しているTypeを提供できるWatch face Complicatinsのみが表示可能です。

データのタイプはComplicationDataクラス内にStaticな定数がTYPE_SHORT_TEXTというような名前で用意されているためそれを使って比較することができます。

データのタイプ


Typeの種類は次のようなものが有ります。
Typeごとに必ずデータを渡す必要がある必須項目と、データを渡してもいいし渡さなくても良い任意項目が決められています。

SHORT_TEXT

必須項目
Short text 短いテキスト(7文字以下)

任意項目
Icon アイコン
Short title タイトル(7文字以下)

用途
短いテキストに加えてアイコンやタイトルも表示できます。
アイコンは単色である必要があります。またWatch faceによって色塗りされることが有ります。
そのため、色に意味をもたせるのではなく、シンプルな図形で意味をもたせる必要があります。

ICON

必須項目
Icon

任意項目
なし

用途
アイコンのみを表示します。SHORT_TEXTと違ってテキストなどを表示できませんが、明確な内容の場合はこちらのほうが見やすいかもしれません。
アイコン画像にはVector画像を使うことができます。

RANGED_VALUE

必須項目
Value 値
Min value 最小値
Max value 最大値

任意項目
Icon アイコン
Short text 短いテキスト(7文字以下)
Short title 短いタイトル(7文字以下)

用途
ある程度の範囲のうちどの程度の割合なのかを示す事ができます。
たとえば電池の消費量や1日の目標運動量に対する現在値などで使用できます。

LONG_TEXT

必須項目
Long text 長いテキスト

任意項目
Long title 長いタイトル
Icon アイコン
Small image 小さな画像

用途
ちょっとした文章を表示したりするのに使えます。SNSの新着などを表示するのに便利そうです。

SMALL_IMAGE

必須項目
Small Image 小さな画像

任意項目
なし

用途
小さな写真を表示します。アイコンでも写真でもどちらでも可能。

LARGE_IMAGE

必須項目
Large Image 大きな画像

任意項目
なし

用途
Watch face全体を埋めるように表示することを想定しています。スライドショーを作成するなどに使用します。

必須の項目が入っていなかったり、逆に必須の項目にも任意の項目にも指定されていない項目の値を渡すと実行時例外になるため注意が必要です。

今回はSHORT_TEXTに対応したData providerを作るため、typeに渡される値がSHORT_TEXTのときだった場合のみ処理を行います。
SampleComplicationProviderService.java

@Override
public void onComplicationUpdate(int complicationId, int type, ComplicationManager complicationManager) {
int value = (int)(Math.random()*100);
if(type == TYPE_SHORT_TEXT){

}

}


データをセット

ComplicationDataクラスを作成してデータを詰めていきます。
ComplicationDataはComplicationData.Builderクラスを使って作ります。
テキストを渡すときはsetShortText()を呼びます。
引数の型はComplicationTextで、ComplicationText.plainText()を呼ぶことで生成することができます。
変数valueをComplicationDataクラスのインスタンスにセットするには次のようにします。
SampleComplicationProviderService.java

@Override
public void onComplicationUpdate(int complicationId, int type, ComplicationManager complicationManager) {
int value = (int)(Math.random()*100);
if(type == TYPE_SHORT_TEXT){
ComplicationData complicationData = new ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT)
.setShortText(ComplicationText.plainText(String.valueOf(value)))
.build();
}

}


complicationManagerのupdateComplicationData()を呼んで、引数にデータがセットされたcomplicationDataをcomplicationIdとともに渡します。
SampleComplicationProviderService.java

@Override
public void onComplicationUpdate(int complicationId, int type, ComplicationManager complicationManager) {
int value = (int)(Math.random()*100);
if(type == TYPE_SHORT_TEXT){
ComplicationData complicationData = new ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT)
.setShortText(ComplicationText.plainText(String.valueOf(value)))
.build();
complicationManager.updateComplicationData(complicationId, complicationData);
}

}

コード全体だと次の通り
SampleComplicationProviderService.java

package org.firespeed.dataproviders;

import android.support.wearable.complications.ComplicationData;
import android.support.wearable.complications.ComplicationManager;
import android.support.wearable.complications.ComplicationProviderService;
import android.support.wearable.complications.ComplicationText;

import static android.support.wearable.complications.ComplicationData.TYPE_SHORT_TEXT;

/**
* Complication Watch face DataProviderがデータを提供するためのサービス
*/
public class SampleComplicationProviderService extends ComplicationProviderService {
/**
* データを返してほしいときに呼ばれる
*
* @param complicationId 画面上に置かれたWatch face Complicationごとに一意となるID
* @param type 表示するデータのType
* @param complicationManager ComplicationManager 結果を渡す
*/
@Override
public void onComplicationUpdate(int complicationId, int type, ComplicationManager complicationManager) {
int value = (int) (Math.random() * 100);
if (type == TYPE_SHORT_TEXT) {
ComplicationData complicationData = new ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT)
.setShortText(ComplicationText.plainText(String.valueOf(value)))
.build();
complicationManager.updateComplicationData(complicationId, complicationData);
}

}
}


Android Manifestの設定

Serviceの宣言

次にAndroidManifestにServiceをセットします。
AndroidManifest.xml

<service android:name=".SampleComplicationProviderService"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name">
</service>


Intent Filterの指定

システムがData providerに最新のデータを要求するためにACTION_COMPLICATION_UPDATE_REQUEST Intentを発行するので、ServiceにIntent Filterを設定してこれを取得します。
AndroidManifest.xml

<intent-filter>
<action android:name="android.support.wearable.complications.ACTION_COMPLICATION_UPDATE_REQUEST"/>
</intent-filter>


サポートするTypeの指定

サポートするタイプとしてSHORT_TEXTを指定します。
複数のタイプを指定するときはカンマでつなぎます。
AndroidManifest.xml

<meta-data android:name="android.support.wearable.complications.SUPPORTED_TYPES"
android:value="SHORT_TEXT"/>


データ更新間隔の指定

データの更新頻度を秒数で指定します。
秒数の感覚が短すぎるとバッテリーへの悪影響を及ぼすので可能な限り長い時間にすることが推奨されています。
一定間隔の更新を行わず、更新タイミングを都度プログラム上で指定する場合には0を指定します。

今回はテストのために1を指定しましょう。
AndroidManifest.xml

<meta-data android:name="android.support.wearable.complications.UPDATE_PERIOD_SECONDS" android:value="1"/>

AndroidManifest全体は次のようになります。
AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.firespeed.dataproviders">

<uses-feature android:name="android.hardware.type.watch" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@android:style/Theme.DeviceDefault">
<service android:name=".SampleComplicationProviderService"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.support.wearable.complications.ACTION_COMPLICATION_UPDATE_REQUEST"/>
</intent-filter>
<meta-data android:name="android.support.wearable.complications.SUPPORTED_TYPES"
android:value="SHORT_TEXT"/>
<meta-data android:name="android.support.wearable.complications.UPDATE_PERIOD_SECONDS" android:value="1"/>
</service>
</application>
</manifest>


build.gradle

build.gradleはこんな感じ
app/build.gradle

apply plugin: 'com.android.application'

android {
compileSdkVersion 24
buildToolsVersion "24.0.2"
defaultConfig {
applicationId "org.firespeed.dataproviders"
minSdkVersion 24
targetSdkVersion 24
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.google.android.support:wearable:2.0.0-alpha3'
compile 'com.google.android.gms:play-services-wearable:9.6.1'
}


実行する

Activity自動実行の無効化

今回はActivityが存在しないため、通常のRunを実行しようとするとエラーになります。
Run時にActivityを起動しないようにRun-EditConfigurationsを開きます。
Android appのwearを選び、Launch OptionsのLaunchでNothingを選びます。


APKインストール

実行モジュールがwearになっていることを確認し実行します。
対象端末をWear端末にしてAPKがインストールされるのを確認してください。

Activityがないため、インストールが終わってもアプリは自動実行されません。

Watch face Complicationsに対応しているWatch faceへの切り替え

最初にWatch face Complicationsに対応しているWatch faceを選択してWatch faceを追加する必要があります。
標準で用意されているWatch faceの中ではデジタルエレメントおよびアナログエレメントがWatch face Complicationsに対応しています。
スクリーンを右から左にフリックして、お気に入りに追加を選び

デジタルエレメントあるいはアナログエレメントを選択します。


ギアを選びWatch faceの設定画面を開きます。


LARGE IMAGE以外のWatch face Complicationsはデータで追加できます。


Watch face Complicationを表示する場所を選択します。


追加するWatch face Complicationを選択します。
今回作成したDataProvidersを選びます。


Watch face Complicationが追加されました。


Watch face Complicationsが追加されランダムな数字が表示されることが確認できます。
データの更新は1秒間隔としていましたが、実際には1秒毎に更新されることはなく2分ほどの間隔になります。
このようにWatch face Complicationはデータを提供するだけで実際にデータの更新要求を出すのはシステムで、実際の描画はWatch faceに依存することになります。


次回はスマートフォンと協調動作してスマートフォン側で指定した値をスマートフォン側のタイミングでデータを更新する仕組みを作ってみましょう。

参考サイト
Android Developers Watch face Complications

コードサンプル
https://github.com/kenz/ComplicationsDemo1

前:HackerTackle16 開催 次:Android Wear2.0 Watch face Complicationsを作るDataSync編

関連キーワード

[Android][Java][モバイル][IT][ウェアラブル]

コメント

名前:名無しさん|投稿日:2016/10/18 11:59

非常に参考になりました。
現在『Complication services(Android Sample) Watch Face プログラム」を改良しています。

質問ですが、
1)Complication services(Android Sample) Watch Face プログラム
  からコールできますか?
2) アナログエレメントWatch Faceでは、[Syatemprovider-appProvider(User App etc)]
コールできますが。
 Complication services(Android Sample) Watch Face プログラムでは、できません。
 ご存知であれば教えてください。


以上

名前:Kenz|投稿日:2016/11/10 03:31

Complication services(Android Sample) Watch Face プログラム については対応しているComplication TypeがSHORT_TEXTだけなんですよね
今回のサンプルアプリはTypeがICONになっているためサンプルのWatch Faceプログラムでは表示することが出来ません。
次回は複数のTypeをサポートする方法について紹介する予定ですので、次回の実装で表示することが出来るようになります。

コメントを投稿する

名前URI
コメント