AndroidアプリをSONY SmartWatch 2に対応させる準備

#はじめに
今更感が拭えませんが、SONY SmartWatch 2への対応に関する記事です。ここではGradleを用いたAndroidアプリのプロジェクトにSONY SmartWatch 2 (以下SW2)のSDKを導入する手順を示します。SW2のSDK自体は次に示す開発者向けのページに従って導入されているものとします。
[Detailed instructions | Sony Developer World](https://developer.sony.com/develop/smartphones-and-tablets/camera-add-on-small-apps-or-ir-remote-apis/install/detailed-instructions/)

開発環境にはIntelliJ IDEA 14を使用し、コンパイルおよびターゲットのSDKのバージョンは19 (Android 4.4.2)としています。SW2のSDKがAndroid 4.4.2 'API 19)の項目に表示されているため、最新バージョンの22ではなく19を指定しています。アプリのモジュール名は`app`としています。

#SDKのコピー
上記の開発者向けページに従ってSW2のSDKを導入すると、AndroidのSDK内の`add-ons\addon-sony_add-on_sdk_3_0-sony-19\samples`にAPIを含んだソースコードが展開されます。必要なファイルは`SmartExtensions`内の`SmartExtensionAPI`および`SmartExtensionUtils`です。プロジェクト内に`sdk`フォルダを作成し、この2つのフォルダをその中にコピーします。(プロジェクトフォルダを散らかさない目的で`sdk`フォルダに入れているだけで、プロジェクトルートでもかまいません。)

#Gradleファイルの用意
`SmartExtensionAPI`には既に`build.gradle`が用意されていますが、コンパイルに使用するSDKとビルドツールのバージョンをAndroid 4.4.2のものに変更しておきます。(元のバージョンが18と指定されており、アプリのプロジェクトとバージョンを揃えたかったためです。)

```groovy:sdk\SmartExtensionAPI\build.gradle
apply plugin: 'android-library'

dependencies {
    compile fileTree(dir: 'libs', include: '*.jar')
}

android {
    compileSdkVersion 19                            // 変更
    buildToolsVersion "19.1.0"                      // 変更

    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
        }

    }
}
```

一方で、`SmartExtensionUtils`には`build.gradle`は用意されていません。`SmartExtensionAPI`と同じ構成なので、上記の`build.gradle`をコピーして使用することにします。ただし、`SmartExtensionUtils`は`SmartExtensionAPI`に依存関係があるため、`dependencies`にその点を追記する必要があります。

```groovy:sdk\SmartExtensionUtils\build.gradle
apply plugin: 'android-library'

dependencies {
    compile fileTree(dir: 'libs', include: '*.jar')
    compile project(':SmartExtensionAPI')           // 追加
}

android {
    compileSdkVersion 19
    buildToolsVersion "19.1.0"

    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
        }

    }
}
```

#ルートプロジェクトに追加
つづいて、この2つのモジュールを追加するためルートプロジェクトの`settings.gradle`を書き換えます。モジュールを追加するには`include`にモジュール名を追記するだけで大丈夫ですが、ルートではなく`sdk`フォルダ内にコピーしているため、プロジェクトフォルダを別途指定する必要があります。それが2、3行目にある`project('~').projectDir = new File('~')`の役割です。

```groovy:settings.gradle
include ':app', ':SmartExtensionAPI', ':SmartExtensionUtils'                     // 変更
project(':SmartExtensionAPI').projectDir = new File('sdk/SmartExtensionAPI')     // 追加
project(':SmartExtensionUtils').projectDir = new File('sdk/SmartExtensionUtils') // 追加
```

最後に、SW2のSDKをアプリで使用するために、アプリの`build.gradle`に依存関係を追記して、Gradleを同期させます。Intellij IDEAなら上に表示されるSync Nowをクリックするだけで大丈夫なはずです。

```groovy:app\build.gradle
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:1.1.1'
    }
}
apply plugin: 'com.android.application'

repositories {
    jcenter()
}

android {
    compileSdkVersion 19
    buildToolsVersion "19.1.0"

    defaultConfig {
        applicationId "net.leak4mk0.sw2sampleapplication.app"
        minSdkVersion 9
        targetSdkVersion 19
        versionCode 1
        versionName "1.0"
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_6
        targetCompatibility JavaVersion.VERSION_1_6
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile project(':SmartExtensionUtils')                                                    // 追加
    compile 'com.android.support:support-v4:19.1.0'
    compile 'com.android.support:appcompat-v7:19.1.0'
}
```

#実装の準備
プロジェクト側の準備は完了しました。次は実装側の準備を行います。SW2のSDKは`Service`を立ち上げ、SW2のホストアプリケーションと`Intent`を利用して通信し、SW2に画面を描画したり、入力を取得したりします。そのため、`Service`と`BroadcastReceiver`の実装、`AndroidManifest.xml`への修正が必要となります。

`Service`はSDKに含まれる`ExtensionService`を継承して実装します。今回は準備ということで、特に細かい実装はしません。クラス名は`WatchExtensionService`としました。

```java:app\src\main\...\WatchExtensionService.java
package net.leak4mk0.sw2sampleapplication.app;

import com.sonyericsson.extras.liveware.extension.util.ExtensionService;
import com.sonyericsson.extras.liveware.extension.util.registration.RegistrationInformation;

public class WatchExtensionService extends ExtensionService {
    @Override
    protected RegistrationInformation getRegistrationInformation() {
        return null;
    }

    @Override
    protected boolean keepRunningWhenConnected() {
        return false;
    }
}
```

さらに`BroadcastReceiver`を継承して`WatchExtensionReceiver`クラスを作成します。受信した`Intent`をすべて上で作成した`WatchExtensionService`に送り付ける実装になっています。基本的に、このクラスはこれ以上実装することはありません。

```java:app\src\main\...\WatchExtensionReceiver.java
package net.leak4mk0.sw2sampleapplication.app;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class WatchExtensionReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        intent.setClass(context, WatchExtensionService.class);
        context.startService(intent);
    }
}
```

ここで実装した`Service`と`BroadcastReceiver`を有効にするため、`AndroidManifest.xml`に修正を加えます。

```xml:app\src\main\AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="net.leak4mk0.sw2sampleapplication.app" >

    <!-- 追加 -->
    <uses-permission android:name="com.sonyericsson.extras.liveware.aef.EXTENSION_PERMISSION"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!-- 追加 -->
        <service android:name=".service.WatchExtensionService"/>
        <service android:name="com.sonyericsson.extras.liveware.extension.util.TunnelService">
            <intent-filter>
                <action android:name="com.sonyericsson.extras.liveware.aef.tunnel.action.BIND"/>
            </intent-filter>
        </service>

        <!-- 追加 -->
        <receiver android:name=".service.WatchExtensionReceiver">
            <intent-filter>
                <action android:name="com.sonyericsson.extras.liveware.aef.registration.EXTENSION_REGISTER_REQUEST"/>
                <action android:name="com.sonyericsson.extras.liveware.aef.registration.ACCESSORY_CONNECTION"/>
                <action android:name="android.intent.action.LOCALE_CHANGED"/>
                <action android:name="com.sonyericsson.extras.liveware.aef.notification.VIEW_EVENT_DETAIL"/>
                <action android:name="com.sonyericsson.extras.liveware.aef.notification.REFRESH_REQUEST"/>
                <action android:name="com.sonyericsson.extras.aef.widget.START_REFRESH_IMAGE_REQUEST"/>
                <action android:name="com.sonyericsson.extras.aef.widget.STOP_REFRESH_IMAGE_REQUEST"/>
                <action android:name="com.sonyericsson.extras.aef.widget.ONTOUCH"/>
                <action android:name="com.sonyericsson.extras.liveware.extension.util.widget.scheduled.refresh"/>
                <action android:name="com.sonyericsson.extras.aef.control.START"/>
                <action android:name="com.sonyericsson.extras.aef.control.STOP"/>
                <action android:name="com.sonyericsson.extras.aef.control.PAUSE"/>
                <action android:name="com.sonyericsson.extras.aef.control.RESUME"/>
                <action android:name="com.sonyericsson.extras.aef.control.ERROR"/>
                <action android:name="com.sonyericsson.extras.aef.control.KEY_EVENT"/>
                <action android:name="com.sonyericsson.extras.aef.control.TOUCH_EVENT"/>
                <action android:name="com.sonyericsson.extras.aef.control.SWIPE_EVENT"/>
                <action android:name="com.sonyericsson.extras.aef.control.OBJECT_CLICK_EVENT"/>
                <action android:name="com.sonyericsson.extras.aef.control.LIST_REFERESH_REQUEST"/>
                <action android:name="com.sonyericsson.extras.aef.control.LIST_REQUEST_ITEM"/>
                <action android:name="com.sonyericsson.extras.aef.control.LIST_ITEM_CLICK"/>
                <action android:name="com.sonyericsson.extras.aef.control.LIST_ITEM_SELECTED"/>
                <action android:name="com.sonyericsson.extras.aef.control.MENU_ITEM_SELECTED"/>
                <action android:name="com.sonyericsson.extras.aef.control.MENU_SHOW"/>
            </intent-filter>
        </receiver>
    </application>

</manifest>
```

#さいごに
以上で実装の直前まで準備できました。ここからは`WatchExtensionService`クラスに手を入れて、ホストアプリケーションへのアプリの登録やSW2に表示する画面の実装を行っていきます。この点に関してはまた今後書いていきたいと思います。