前回はスマートフォンからデータを取得してSHORT_TEXTを出力するData Provider を作成しました。
基礎編で紹介したとおり、Complicationsのデータ型にはいくつかのタイプがあります。
Data Providerは複数のデータタイプを提供することができ、一方でWatch Faceも複数のデータタイプをサポートできます。
目次
基礎DataSync
多データ型対応
複数のデータタイプへ対応
Watch Face上にデータを表示できるようにするためには、Watch FaceとData Providerの両方が対応しているデータタイプが最低でも1種類以上ある必要があります。そのため、Data Providerは可能な限り多様な種類のデータタイプをサポートすることで、より多くのWatch Faceに対応することができます。
今回は前回のSHORT_TEXTに加えてICONとRANGE_VALUEに対応してみます。
Android Manifestの修正
複数のタイプに対応することを宣言するためにAndroidManifestのandroid:name="android.support.wearable.complications.SUPPORTED_TYPESにあるandroid:valueへカンマ区切りでテキストを繋げていきます。ICONとRANGE_VALUEに対応することを宣言するため以下のように変更します。
AndroidManifest.xml
<meta-data
android:name="android.support.wearable.complications.SUPPORTED_TYPES"
android:value="SHORT_TEXT,ICON,RANGED_VALUE" />
アイコンの作成
今回はICONをサポートするためにICONファイルを作成します。アイコンはWatchFaceによっては透明度のみが使用され色情報が無視されたり別の色に置き換えられる場合が有ります。
このガイドラインは様々なデザインのWatchFace上で正しくアイコンを表示するために重要です。そのためアイコンはシルエットのみで情報が伝わるようにする必要があります。
Android Wear2.0はAPIレベル24になるため、Vector形式のアイコンを心配なく使用することができます。
Vector形式とすることで複数のピクセル密度を意識しなくて良くなる他、ファイルサイズの縮小やメンテナンスを容易にすることができます。
AndroidのVector形式は特殊な形式ですが、Android Studioを使えば簡単にSVGから変換して作成することができます。
プロジェクトで右クリックしNew-Vector Assetを選びます。

Android Studioで用意しているアイコンを使用する場合はMaterial Icon、独自のSVGやPSDから変換を行いたい場合はLocal fileを選びます。
今回はMaterial Iconの上矢印と下矢印のアイコンを作成し、50未満と50以上でアイコンが切り替わるようにします。
Iconをクリックして上矢印のアイコンを選択しNameにic_upと入力したらNextをクリックします。

Confirm Icon Pathが出たらFinishをクリックします。

同様に下矢印のアイコンも作成してic_downという名前にします。

アイコンへの着色
現在テストで使っているElements Analogはアイコンを色付けしないため、アイコンを見やすくするために色を白にしておきます。ic_up、ic_down共に次のようにfillColorの値を#FFFFFFに置き換えます。
ic_up.xml
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFF"
android:pathData="M4,12l1.41,1.41L11,7.83V20h2V7.83l5.58,5.59L20,12l-8,-8 -8,8z"/>
</vector>
ic_down.xml
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:pathData="M20,12l-1.41,-1.41L13,16.17V4h-2v12.17l-5.58,-5.59L4,12l8,8 8,-8z"
android:fillColor="#FFFFFF"/>
</vector>
SampleComplicationProviderServiceの修正
求められるTypeにより出力する形式を変更するために、SampleComplicationProviderServiceのtypeを比較する条件式にTYPE_ICONとTYPE_RANGED_VALUEの場合を加えます。SampleComplicationProviderService
@Override
public void onComplicationUpdate(int complicationId, int type, ComplicationManager complicationManager) {
SharedPreferences data = getSharedPreferences("pref", Context.MODE_PRIVATE);
int value = data.getInt("value",0);
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);
}else if(type == TYPE_ICON){
}else if(type == TYPE_RANGED_VALUE){
}
}
TYPE_ICONの場合の値を埋めていきます。
ComplicationData.Builderを呼び出し、引数はTYPE_ICONとします。
ComplicationData complicationData = new ComplicationData.Builder(ComplicationData.TYPE_ICON)
ComplicationData.Builder内で渡された値がデータ型と一致するかの検証を行っており、値の過不足があった場合は実行時例外が発生します。
データ型ごとに必須の値を必ず設定し、必須と任意項目以外の値を渡さないように気をつけてください。
例えばデータ型がTYPE_ICONだった場合setShortText()は許可されていないため使用すると例外が発生します。
TYPE_ICONではアイコンだけを設定可能です。
drawableからICONを作成するにはIcon.createWithResourceを使用します。
引数として第一引数にContext、第二引数にdrawableのidを指定します。
SampleComplicationProviderService
}else if(type == TYPE_ICON){
ComplicationData complicationData = new ComplicationData.Builder(ComplicationData.TYPE_ICON)
.setIcon(Icon.createWithResource(this, value<50?R.drawable.ic_down:R.drawable.ic_up))
.build();
complicationManager.updateComplicationData(complicationId, complicationData);
同様にTYPE_RANGED_VALUEの処理を設定します。
TYPE_RANGED_VALUEではvalue, minValue, maxValueが必須項目でIconなど幾つかの値を任意で書き込むことができます。
すべてを組み込むと次の通り。
SampleComplicationProviderService
@Override
public void onComplicationUpdate(int complicationId, int type, ComplicationManager complicationManager) {
SharedPreferences data = getSharedPreferences("pref", Context.MODE_PRIVATE);
int value = data.getInt("value",0);
if (type == TYPE_SHORT_TEXT) {
ComplicationData complicationData = new ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT)
.setShortText(ComplicationText.plainText(String.valueOf(value)))
.setIcon(Icon.createWithResource(this, value<50?R.drawable.ic_down:R.drawable.ic_up))
.build();
complicationManager.updateComplicationData(complicationId, complicationData);
}else if(type == TYPE_ICON){
ComplicationData complicationData = new ComplicationData.Builder(ComplicationData.TYPE_ICON)
.setIcon(Icon.createWithResource(this, value<50?R.drawable.ic_down:R.drawable.ic_up))
.build();
complicationManager.updateComplicationData(complicationId, complicationData);
}else if(type == TYPE_RANGED_VALUE){
ComplicationData complicationData = new ComplicationData.Builder(ComplicationData.TYPE_RANGED_VALUE)
.setIcon(Icon.createWithResource(this, value<50?R.drawable.ic_down:R.drawable.ic_up))
.setValue(value)
.setMaxValue(100f)
.setMinValue(0f)
.build();
complicationManager.updateComplicationData(complicationId, complicationData);
}
}
これで複数のデータタイプに対応することが可能です。
ユーザーにデータタイプを選択させる。
Watch faceとData providerで複数のデータタイプがマッチする場合、Data typeは自動的に優先度が高いデータタイプが選択されます。例えば、Data providerとWatch faceがどちらもSHORT_TEXTとICONに対応する場合、SHORT_TEXTの方がデータが充実しており優先度が高いとみなされSHORT_TEXT が使用されます。
しかしながら、アイコンだけで十分に状態を表現可能であるが、SHORT_TEXTしか対応していないWatch faceに対応するためにSHORT_TEXTもサポートしたいと言うような場合があります。
このような場合にはユーザーにデータタイプを選択させることが出来ます。
データタイプを選ばせるには、データタイプの種類分それぞれ1つのデータタイプに対応したServiceを作ります。
Serviceを修正する。
複数のデータタイプで、同じデータを出力する場合、Service側の処理はどれも同じという場合が多いです。単に複数のServiceがあれば良いため、もとのServiceを抽象化して、データタイプごとに継承して複数のServiceに分けます。
SampleComplicationProviderService
public class SampleComplicationProviderService extends ComplicationProviderService {
public static class TypeText extends SampleComplicationProviderService{}
public static class TypeIcon extends SampleComplicationProviderService{}
public static class TypeRangedValue extends SampleComplicationProviderService{}
クラス全体だと次のとおりです。
SampleComplicationProviderService
package org.firespeed.dataproviders;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.drawable.Icon;
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_ICON;
import static android.support.wearable.complications.ComplicationData.TYPE_RANGED_VALUE;
import static android.support.wearable.complications.ComplicationData.TYPE_SHORT_TEXT;
/**
* Complication Watch Face DataProviderがデータを提供するためのサービス
*/
public class SampleComplicationProviderService extends ComplicationProviderService {
public static class TypeText extends SampleComplicationProviderService{}
public static class TypeIcon extends SampleComplicationProviderService{}
public static class TypeRangedValue extends SampleComplicationProviderService{}
/**
* データを返してほしいときに呼ばれる
*
* @param complicationId 画面上に置かれたWatch Face Complicationごとに一意となるID
* @param type 表示するデータのType
* @param complicationManager ComplicationManager 結果を渡す
*/
@Override
public void onComplicationUpdate(int complicationId, int type, ComplicationManager complicationManager) {
SharedPreferences data = getSharedPreferences("pref", Context.MODE_PRIVATE);
int value = data.getInt("value",0);
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);
}else if(type == TYPE_ICON){
ComplicationData complicationData = new ComplicationData.Builder(ComplicationData.TYPE_ICON)
.setIcon(Icon.createWithResource(this, value<50?R.drawable.ic_down:R.drawable.ic_up))
.build();
complicationManager.updateComplicationData(complicationId, complicationData);
}else if(type == TYPE_RANGED_VALUE){
ComplicationData complicationData = new ComplicationData.Builder(ComplicationData.TYPE_RANGED_VALUE)
.setIcon(Icon.createWithResource(this, value<50?R.drawable.ic_down:R.drawable.ic_up))
.setValue(value)
.setMaxValue(100f)
.setMinValue(0f)
.build();
complicationManager.updateComplicationData(complicationId, complicationData);
}
}
}
AndroidManifestの修正
あとはAndroidManifestにデータタイプごとのServiceを設定していきます。android:name="android.support.wearable.complications.SUPPORTED_TYPESでタイプを別々に分けることで別のタイプをユーザーが選択できるようになります。
ユーザーが選択しやすいようにiconやlabelを書き換えるのも忘れないようにしてください。
AndroidManifest
<service
android:name=".SampleComplicationProviderService$TypeText"
android:icon="@mipmap/ic_launcher"
android:label="Text">
<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="0" />
</service>
<service
android:name=".SampleComplicationProviderService$TypeIcon"
android:icon="@mipmap/ic_launcher"
android:label="Icon">
<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="ICON" />
<meta-data
android:name="android.support.wearable.complications.UPDATE_PERIOD_SECONDS"
android:value="0" />
</service>
<service
android:name=".SampleComplicationProviderService$TypeRangedValue"
android:icon="@mipmap/ic_launcher"
android:label="RangedValue">
<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="RANGED_VALUE" />
<meta-data
android:name="android.support.wearable.complications.UPDATE_PERIOD_SECONDS"
android:value="0" />
</service>
<service android:name=".ReceiverService">
<intent-filter>
<action android:name="com.google.android.gms.wearable.MESSAGE_RECEIVED" />
<data
android:host="*"
android:pathPrefix="/path"
android:scheme="wear" />
</intent-filter>
</service>
ComplicationでDataProvicerを選ぶとさらにどのデータタイプにするかを選択する画面が出てくるようになります。

SHORT_TEXT

ICON

RANGED_VALUE
