Skip to content

DevicePlugin Manual for Android Studio 100

TakayukiHoshi1984 edited this page Aug 5, 2020 · 2 revisions

##目次

Device Connectシステムは、マルチOS、マルチプラットフォームのランタイム環境上において、スマートデバイスと接続するためのAPI (RESTful) を提供します。これにより、スマートデバイスとの接続方法・連携方法の利便性を向上することを目的としています。 
Device Connectシステムが提供する機能一覧は以下の通りです。

  • 連携可能な周辺機器一覧を表示する機能を提供
  • 接続I/F(Bluetooth, BLE, Wi-Fi, NFC)の違いのわかりにくさを解消
  • 機器プロファイルによる統一的なAPIの提供

本書は、Device Connect Managerとスマートデバイスを接続するためのデバイスプラグイン(Android版)を作成するためのチュートリアルです。

本チュートリアルでは、

  • ServiceDiscoveryプロファイル
  • ServiceInformationプロファイル
  • Systemプロファイル

を用いた簡単なデバイスプラグインのサンプルを作成します。
この3つのプロファイルはデバイスプラグインを作成する上で必須となるプロファイルになります。

また、これら必須プロファイル以外に2つのプロファイルを実装します。

  • Ambientプロファイル
  • Pressureプロファイル

これらのプロファイルを組み込んだデバイスプラグンを完成させるには、以下の6つのコードを作成する必要があります。

ソースコード名 役割
ExampleDeviceServiceProvider.java RESTfulで送られてくる命令をDevice Connect Manager経由で受け取るBroadcast Receiver。
ExampleDeviceService.java 受け取った命令を解釈し、各プロファイルに配送する処理を行うService。
ExampleServiceDiscoveryProfile.java ServiceDeiscorveryプロファイルの処理。接続しているデバイス一覧を返却します。
ExampleSystemProfile.java Systemプロファイルの処理。
ExampleAmbientProfile.java Ambientプロファイルの処理。照度を取得することができるようになります。
ExamplePressureProfile.java Pressureプロファイルの処理。気圧を取得することができるようになります。

Android版のデバイスプラグインとDevice Connect Managerとは、Intentを通して連携します。Intentは、デバイスプラグインのBroadcast Receiverが取得し、Serviceに受け渡します。Serviceから、各種Profileの処理を呼び出し、Intentで指示のあった処理をおこない、Device Connect Managerに結果を返信します。

このマニュアルでは、開発環境としてAndroid Studioを使用します。 こちらからダウンロードを行ってください。
チュートリアル作成時のAndroid Studioのバージョンは、2.2.1になっています。

Android StudioのUIは、バージョンによって異なりますのでご注意ください。

Android Studioの[Start a new Android Studio project]を選択します。

Application Name, Project Name, Package Nameを入力します。

項目 設定値
Application Name My Device Plugin
Company Name mydomain
Package Name com.mydomain.mydeviceplugin
Minimum Required SDK 14

[Package name]を編集する場合は、以下の[Edit]を選択することで編集できるようになります。


今回は、Activityがメインではないので、[Add No Activity]を選択してください。


ここまでCreate New Projectに従ってプロジェクトを作成するとプロジェクトが作成されます。

Device Connect デバイスプラグインアプリの開発には、dConnectDevicePluginSDKとdConnectSDKForAndroidの2つが必要になります。

ソースコード名 役割
dConnectSDKForAndroid Device ConnectのRESTFulの処理を提供するAPI群。
dConnectDevicePluginSDK Device Connect本体とのやりとりをおこなうAPI群。

app/build.gradle に以下のレポジトリを登録してください。

android {
    // 省略・・・

    packagingOptions {
        exclude 'META-INF/DEPENDENCIES'
        exclude 'META-INF/LICENSE'
        exclude 'META-INF/NOTICE'
        exclude 'META-INF/NOTICE.txt'
        exclude 'META-INF/LICENSE.txt'
        exclude 'META-INF/DEPENDENCIES.txt'
    }

    // 省略・・・
}

repositories {
    maven { url 'https://raw.githubusercontent.com/DeviceConnect/DeviceConnect-Android/main/dConnectSDK/dConnectSDKForAndroid/repository/' }
    maven { url 'https://raw.githubusercontent.com/DeviceConnect/DeviceConnect-Android/main/dConnectDevicePlugin/dConnectDevicePluginSDK/repository/' }
}

dependencies {
    // 省略・・・
    compile 'org.deviceconnect:dconnect-device-plugin-sdk:1.0.1'
}

build.gradleの修正しましたら、以下のボタンを押下してプロジェクトを同期します。

ここまで、デバイスプラグインのビルド環境が整います。

Android Studioの[app]>[Edit Configurations]を選択します。

ActivityをDo not launch Activityに指定します。

これで、デバイスプラグインの起動準備が整います。

ExampleDeviceProvider.javaでは、DConnectMessageServiceProviderを継承(Extend)します。DConnectMessageServiceProviderを継承することで、Device Connect Managerからの通知を受け付けることが可能になります。

ExampleDeviceServiceProvider.javaとAndroidManifest.xmlを作成していきます。

サンプルコード

ExampleDeviceServiceProvider.java

package com.mycompany.devicepluginsample1;

import android.app.Service;
import org.deviceconnect.android.message.DConnectMessageServiceProvider;

public class ExampleDeviceServiceProvider<T extends Service> extends DConnectMessageServiceProvider<Service> {
    @SuppressWarnings("unchecked")
    @Override
    protected Class<Service> getServiceClass() {
        Class<? extends Service> clazz = (Class<? extends Service>) ExampleDeviceService.class;
        return (Class<Service>) clazz;
    }
}

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.mycompany.devicepluginsample1">

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >

        <!-- Device Connect Example Device Plugin Provider. -->
        <receiver android:name=".ExampleDeviceServiceProvider" >
            <meta-data
                android:name="org.deviceconnect.android.deviceplugin"
                android:resource="@xml/deviceplugin" />

            <intent-filter>
                <action android:name="org.deviceconnect.action.GET" />
                <action android:name="org.deviceconnect.action.PUT" />
                <action android:name="org.deviceconnect.action.POST" />
                <action android:name="org.deviceconnect.action.DELETE" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

ファイルの作成について

パッケージ(com.mycompany.devicepluginsample1)を選択し、ショートカットメニューで、[New]-[Java Class]を選択します。

ExampleDeviceServiceProvider.javaでは、ConnectMessageServiceProviderを継承しますが、Class<Service> getServiceClass()をコード内に実装するルールになっています。 Class<Service> getServiceClass()で、ExampleDeviceServiceProviderが受け取ったDevice Connect Messageを、指定したServiceに対して配送することができるようになります。

ExampleDeviceServiceProvider.java

package com.mycompany.devicepluginsample1;

import android.app.Service;
import org.deviceconnect.android.message.DConnectMessageServiceProvider;

public class ExampleDeviceServiceProvider<T extends Service> extends DConnectMessageServiceProvider<Service> {
    @SuppressWarnings("unchecked")
    @Override
    protected Class<Service> getServiceClass() {
        Class<? extends Service> clazz = (Class<? extends Service>) ExampleDeviceService.class;
        return (Class<Service>) clazz;
    }
}

ExampleDeviceServiceProvider.javaは、Broadcast Receiverとして組み込まれるために、AndroidManifest.xmlのapplicationタグ内にreceiverタグを追加します。

タグ内のmeta-dataタグのname属性に『org.deviceconnect.android.deviceplugin』が設定されている場合にデバイスプラグインとして処理が行われます。この定義がない場合にはDevice Connect Managerは処理を行わないので、充分に注意してください。

また、タグ内のIntent-filterタグに記述されたActionそれぞれがHTTPメソッドのGET, PUT, POST, DELETEに対応しており、これらの項目の有無によって、特定のHTTPメソッドのメッセージのみ受け取る様に設定できます。

  • org.deviceconnect.action.GET
  • org.deviceconnect.action.PUT
  • org.deviceconnect.action.POST
  • org.deviceconnect.action.DELETE

AndroidManifest.xml

        <!-- Device Connect Example Device Plugin Provider. -->
        <receiver android:name=".ExampleDeviceServiceProvider" >
            <meta-data
                android:name="org.deviceconnect.android.deviceplugin"
                android:resource="@xml/deviceplugin" />

            <intent-filter>
                <action android:name="org.deviceconnect.action.GET" />
                <action android:name="org.deviceconnect.action.PUT" />
                <action android:name="org.deviceconnect.action.POST" />
                <action android:name="org.deviceconnect.action.DELETE" />
            </intent-filter>
        </receiver>

Device Connectのデバイスプラグインとして、サービスを動作させるためのres/xml/deviceplugin.xmlに提供プロファイル一覧を記載するルールになっています。

res/xml以下にdeviceplugin.xmlを作成します。 初期の状態ではxmlディレクトリは存在していませんので、まずはresにxmlディレクトリを作成します。

次に、deviceplugin.xmlを作成します。

deviceplugin.xmlを開き、以下の記述を追加してください。

deviceplugin.xml

<?xml version="1.0" encoding="UTF-8"?>
<deviceplugin-provider xmlns:android="http://schemas.android.com/apk/res/android">
	<profile name="servicediscovery" />
    <profile name="serviceinformation" />
    <profile name="system" />
</deviceplugin-provider>

deviceplugin.xmlには、デバイスプラグインで使用するプロファイルの一覧を指定します。 ここに宣言されているプロファイル以外は使用することができないので、注意してください。

次に、nameタグやdescriptionタグなどの説明を行います。 独自拡張プロファイルを追加した場合には、これらのタグの追加を行う必要があります。 タグが無かった場合には、認証画面においてプロファイルの名前がそのまま表示されてしまい分かりづらくなります。

独自拡張していないDevice Connectで定義されているプロファイルについては、システムがタイトルや詳細説明をもっているので、新たに定義する必要はありません。

タグ 属性 子タグ 説明
profile name description デバイスプラグインで使用するプロファイルを定義します。独自プロファイルなどを定義したときもこのタグを追加する必要があります。
name system プロファイル名を定義します。
name 認証ダイアログに表示するタイトル名を定義します。ただし、Device Connectで標準化しているプロファイルについては、システム側でタイトル名や説明が定義されているので、ここで追加する必要はありません。
lang ja 言語指定を行います。BCP-47にある言語タグ多言語対応を行うために、言語ごとにタイトルを設定することができます。端末側が要求する言語が定義されていなかった場合は、最初に見つかったnameタグのタイトルを表示します。
description プロファイルの詳細説明を定義します。ただし、Device Connectで標準化しているプロファイルについては、システム側でタイトル名や説明が定義されているので、ここで追加する必要はありません。
lang ja 言語指定を行います。BCP-47にある言語タグ多言語対応を行うために、言語ごとに詳細説明を設定することができます。端末側が要求する言語が定義されていなかった場合は、最初に見つかったdescriptionタグの詳細を表示します。

他言語化に対応した場合の、deviceplugin.xmlの記載例)

<?xml version="1.0" encoding="UTF-8"?>
<deviceplugin-provider xmlns:android="http://schemas.android.com/apk/res/android" >
    <profile name="my_profile">
        <name lang="ja-JP">日本語</name>
        <description lang="ja-JP">日本語の説明</description>
        <name lang="en-US">English</name>
        <description lang="en-US">English explain</description>
    </profile>
    <profile name="servicediscovery" />
    <profile name="serviceinformation" />
    <profile name="system" />
</deviceplugin-provider>

ExampleDeviceService.javaは、DConnectMessageServiceを継承します。DConnectMessageServiceは、Device Connect Managerから送られてきたDConnectメッセージを処理するためのサービスです。

サンプルコード

ExampleDeviceService.java

package com.mycompany.devicepluginsample1;

import org.deviceconnect.android.message.DConnectMessageService;
import org.deviceconnect.android.profile.ServiceDiscoveryProfile;
import org.deviceconnect.android.profile.ServiceInformationProfile;
import org.deviceconnect.android.profile.SystemProfile;

/**
 * サンプルのdConnectメッセージサービス実装クラス.
 * @author docomo
 */
public class ExampleDeviceService extends DConnectMessageService {
    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    protected SystemProfile getSystemProfile() {
        return new ExampleSystemProfile();
    }

    @Override
    protected ServiceInformationProfile getServiceInformationProfile() {
        return new ServiceInformationProfile(this){};
    }

    @Override
    protected ServiceDiscoveryProfile getServiceDiscoveryProfile() {
        return new ExampleServiceDiscoveryProfile();
    }
}

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.mycompany.deviceplugin">

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >

        <!-- Device Connect Plugin Provider. -->
        <receiver android:name=".ExampleDeviceServiceProvider" >
            <meta-data
                android:name="org.deviceconnect.android.deviceplugin"
                android:resource="@xml/deviceplugin" />

            <intent-filter>
                <action android:name="org.deviceconnect.action.GET" />
                <action android:name="org.deviceconnect.action.PUT" />
                <action android:name="org.deviceconnect.action.POST" />
                <action android:name="org.deviceconnect.action.DELETE" />
            </intent-filter>
        </receiver>

        <!-- Device Connect Plugin Service. -->
        <service
            android:name=".ExampleDeviceService"
            android:exported="false" >
        </service>
    </application>
</manifest>

ファイルの作成

ExampleDeviceService.javaを新規に作成します。

このクラスを実装することでデバイスプラグインを動作させる事が出来ます。

また、AndroidManifest.xmlもExampleDeviceServiceの追加に伴い、applicationタグ内にserviceタグを追加します。

        <!-- Device Connect Plugin Service. -->
        <service
            android:name=".ExampleDeviceService"
            android:exported="false" >
        </service>

それでは、事前準備が整ったので、各プロファイルの実装を行います。

今回実装するプロファイルは、

  • ServiceDiscoveryプロファイル
  • ServiceInformationプロファイル
  • Systemプロファイル

の3個になります。

プロファイルの実装は、DConnectProfileを継承して作成することになります。プロファイルには、API規定済みプロファイルと、独自拡張プロファイルの2種類があります。API規定済みプロファイルでは、すでに標準的なAPIが策定されているので、APIを拡張する場合は、APIに関連するメソッドをオーバーライドすることで、実装します。独自拡張プロファイルは、DConnectProfileのみ継承し、そこから独自に実装をおこないます。

API規定済みプロファイル

  • Batteryプロファイル
  • Connectプロファイル
  • DeviceOrientationプロファイル
  • FileDescriptorプロファイル
  • Fileプロファイル
  • Lightプロファイル
  • MediaPlayerプロファイル
  • MediaStreamRecordingプロファイル
  • Notificationプロファイル
  • Phoneプロファイル
  • Proximityプロファイル
  • ServiceDiscoveryプロファイル
  • ServiceInformationプロファイル
  • Settingsプロファイル
  • Systemプロファイル
  • Vibrationプロファイル

これらのクラスを継承して、各デバイスプラグインの処理を実装してください。

ServiceDiscovery プロファイルは、Device Connect Managerにデバイスの有無を通知するためのプロファイルになります。 このプロファイルが実装が必須のプロファイルになっていますので、実装されていない場合、デバイスプラグインの起動時にエラーが発生します。

サンプルコード

ExampleServiceDiscoveryProfile.java

package com.mycompany.devicepluginsample1;

import android.content.Intent;
import android.os.Bundle;

import org.deviceconnect.android.profile.DConnectProfileProvider;
import org.deviceconnect.android.profile.ServiceDiscoveryProfile;
import org.deviceconnect.message.intent.message.IntentDConnectMessage;

import java.util.ArrayList;
import java.util.List;

/**
 * Service Discovery プロファイルの実装クラス.
 * @author docomo
 */
public class ExampleServiceDiscoveryProfile extends ServiceDiscoveryProfile {
    /** サンプルのサービスID. */
    private static final String SERVICE_ID = "example_service_id";

    /** サンプルのデバイス名. */
    private static final String DEVICE_NAME = "Example Device";

    public ExampleServiceDiscoveryProfile(DConnectProfileProvider provider) {
        super(provider);
    }

    /**
     * Service Discoveryプロファイル.
     * [/servicediscovery]に対応するメソッド.
     * @param request リクエスト
     * @param response レスポンス
     * @return 即座に返却する場合はtrue, 非同期に返却する場合はfalse
     */
    @Override
    public boolean onGetServices(final Intent request, final Intent response) {
        List<Bundle> services = new ArrayList<Bundle>();
        Bundle service = new Bundle();
        setId(service, SERVICE_ID);
        setName(service, DEVICE_NAME);
        setType(service, NetworkType.WIFI);
        setOnline(service, true);
        setScopes(service, getProfileProvider());
        services.add(service);
        setServices(response, services.toArray(new Bundle[services.size()]));
        setResult(response, IntentDConnectMessage.RESULT_OK);
        return true;
    }
}

新規に、ExampleServiceDiscoveryProfile.javaを作成します。

サービスIDは、デバイスプラグインの中でデバイスを識別・管理するためのIDとなります。
このサンプルでは、IDを文字列「example_service_id」としていますが、実際のデバイスプラグインでは、デバイスを一意に識別できるようなIDを指定してください。

また、実装したクラスは、DConnectMessageService#getServiceDiscoveryProfileを実装することで、ConnectMessageServiceに追加されます。

public class ExampleDeviceService extends DConnectMessageService {
    // ・・・省略・・・

    @Override
    protected ServiceDiscoveryProfile getServiceDiscoveryProfile() {
        return new ExampleServiceDiscoveryProfile();
    }
}

ServiceInformationプロファイルでは、デバイスがサポートしているプロファイルの一覧やデバイスの情報をDevice Connect Managerに返却します。

また、実装したクラスは、DConnectMessageService#getServiceInformationProfileを実装することで、DConnectMessageServiceに追加されます。

特に追加の実装がなければ、このサンプルのように空実装でも問題はありません。

public class ExampleDeviceService extends DConnectMessageService {
    // ・・・省略・・・

    @Override
    protected ServiceInformationProfile getServiceInformationProfile() {
        return new ServiceInformationProfile(this) {};
    }
}

Systemプロファイルでは、デバイスプラグインがサポートしているプロファイルの一覧などをDevice Connect Managerに返却します。また、設定用のActivityの呼び出しが可能です。

Systemプロファイルでは、SystemプロファイルのベースクラスであるSystemProfileを継承しています。そのため、多くの処理は、継承により実装済みです。今回は、設定画面の呼び出し用の処理のみ追記します。

また、実装したクラスは、DConnectMessageService#getSystemProfileを実装することで、DConnectMessageServiceに追加されます。

public class ExampleDeviceService extends DConnectMessageService {
    // ・・・省略・・・

    @Override
    protected SystemProfile getSystemProfile() {
        return new ExampleSystemProfile();
    }
}

サンプルコード

ExampleSystemProfile.java

package com.mycompany.devicepluginsample1;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;

import org.deviceconnect.android.profile.DConnectProfileProvider;
import org.deviceconnect.android.profile.SystemProfile;

/**
 * System プロファイル実装クラス.
 * @author docomo
 */
public class ExampleSystemProfile extends SystemProfile {
    @Override
    protected Class<? extends Activity> getSettingPageActivity(Intent request, Bundle param) {
        return SettingActivity.class;
    }
}

新規に、ExampleServiceDiscoveryProfile.javaを作成します。

そのプロファイルを返却する処理はベースクラスであるSystemProfileクラスが行うため、このクラスではコンストラクタを用意する以外、特にすることはありません。 なお、DConnectMessageServiceはDConnectProfileProviderのインタフェースを実装しています。 getSettingActivity()のSettingActivityクラスは、この時点でエラーが出るが、次の章で作成するためそのままとしておきます。

デバイスプラグインでは、各機器との接続設定などを設定画面で行うルールになっています。

ここでは、設定画面を簡単に生成するサンプルの作成方法について説明します。
設定画面は、ExampleSystemProfile#getSettingPageActivityで、SettingActivity.javaを返却していますので、その実装を行います。また、設定画面ではFragmentを用いますので、同時にMyFragment.javaも生成します。

SettingActivity.javaでは、DConnectSettingPageFragmentActivityというクラスを継承し、実装しなければいけないメソッドを実装します。
SettingActivity.javaでは、以下の記述をしてください。

  • getPageCount()
    • 作成する設定画面のページ数を指定します。
  • createPage(int position)
    • 表示するFragmentを設定します。
    • positionには現在表示しているページ数が渡されてくるため、それをBundleなどによってFragmentへ渡します。

SettingActivity.javaからは、MyFragment.javaを呼び出しています。

SettingActivity.java

package com.mycompany.deviceplugin;

import android.os.Bundle;
import android.support.v4.app.Fragment;

import org.deviceconnect.android.ui.activity.DConnectSettingPageFragmentActivity;

public class SettingActivity extends DConnectSettingPageFragmentActivity {
    /**
     * 設定画面のページ数.
     * @return ページ数
     */
    @Override
    public int getPageCount() {
        return 3;
    }

    /**
     * 設定画面のページ.
     * @param position 表示するページ
     * @return 表示するFragment
     */
    @Override
    public Fragment createPage(int position) {
        Bundle b = new Bundle();
        b.putInt("position", position);
        MyFragment f = new MyFragment();
        f.setArguments(b);
        return f;
    }
}

MyFragment.java

package com.mycompany.deviceplugin;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class MyFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Bundle args = getArguments();
        int position = args.getInt("position", -1);
        View root = inflater.inflate(R.layout.fragment, container, false);
        TextView view = (TextView) root.findViewById(R.id.text);
        position++;
        view.setText("Fragment : " + position);
        return root;
    }
}

MyFragment.javaでは、layout情報として、res/layout/fragment.xmlを読み込んでいるので、新規に作成します。

fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

<TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="30sp"
        android:id="@+id/text"/>
</LinearLayout>

この設定画面用ActivityをAndroidManifest.xmlに追加します。

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.mycompany.devicepluginsample1">

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >

        <!-- Device Connect Plugin Provider. -->
        <receiver android:name=".ExampleDeviceServiceProvider" >
            <meta-data
                android:name="org.deviceconnect.android.deviceplugin"
                android:resource="@xml/deviceplugin" />

            <intent-filter>
                <action android:name="org.deviceconnect.action.GET" />
                <action android:name="org.deviceconnect.action.PUT" />
                <action android:name="org.deviceconnect.action.POST" />
                <action android:name="org.deviceconnect.action.DELETE" />
            </intent-filter>
        </receiver>

        <!-- Device Connect Plugin Service. -->
        <service
            android:name=".ExampleDeviceService"
            android:exported="false" >
        </service>

        <!-- Device Connect Plugin Setting -->
        <activity
            android:name=".SettingActivity"
            android:theme="@android:style/Theme.DeviceDefault.Light.DarkActionBar"
            android:exported="false" >
        </activity>
    </application>
</manifest>

デバイスプラグインにプロファイルを追加したい場合、ExampleDeviceService.javaで、DConnectMessageServiceProvider#addProfile(DConnectProfile)でプロファイルを追加します。

必須プロファイルである ServiceDiscoveryProfile、ServiceInformationProfile、SystemProfileは、addProfileではなく、getServiceDiscoveryProfile、getServieInformationProfile、getSystemProfileを実装することで追加することができます。 これらを実装しないとDConnectMessageServiceは動作しませんので、ご注意ください。

サンプルコード

ExampleDeviceService.java

package com.mycompany.devicepluginsample1;

import org.deviceconnect.android.message.DConnectMessageService;
import org.deviceconnect.android.profile.ServiceDiscoveryProfile;
import org.deviceconnect.android.profile.ServiceInformationProfile;
import org.deviceconnect.android.profile.SystemProfile;

/**
 * サンプルのdConnectメッセージサービス実装クラス.
 * @author docomo
 */
public class ExampleDeviceService extends DConnectMessageService {
    @Override
    public void onCreate() {
        super.onCreate(); 
    }

    @Override
    protected SystemProfile getSystemProfile() {
        return new ExampleSystemProfile();
    }

    @Override
    protected ServiceInformationProfile getServiceInformationProfile() {
        return new ServiceInformationProfile(this){};
    }

    @Override
    protected ServiceDiscoveryProfile getServiceDiscoveryProfile() {
        return new ExampleServiceDiscoveryProfile();
    }
}

重い処理を行う場合にはDConnectMessageServiceを使用し、別スレッドを使用するべきで、また上記で説明したようにデバイスプラグインの中でデータを使い回したい場合などもDConnectMessageServiceを使用するべきです。


今作成中のDeviceプラグインのデバッグには、DeviceConnect仮想サーバを内包したDeviceConnectManagerが必要です。DeviceConnectManagerは、互換性のあるManagerであるDeviceWebAPIのManagerが株)GClueより、Google Playで公開されています。

アイコン ダウンロードURL
Device Web API Manager

DeviceWebAPI Managerを起動します。

デバイスプラグイン管理を選択し、デバイスプラグイン一覧の中には、まだ何もプラグインが入っていないことを確認します。

DeviceWebAPI Managerがインストールされている端末に、作成したDeviceプラグインをインストールします。

インストールが完了すると、Deviceプラグインは自動的にDeviceWebAPI Managerのプラグインとして自動認識されます。

それでは、DeviceWebAPI Managerを起動して、Deviceプラグインに接続してみます。

今回は、テスト用に、外部IPからの接続を有効にし、LocalOAuthの認証を無効(認証Dialogは表示)にします。DeviceWeb API Managerと開発用のPCを同一ネットワークに接続し、IPアドレスベースで、お互いが認識できるようにしておきます。

起動時の設定

  • 開発用PCとDeviceWebAPI Managerがインストールされた端末を同一ネットワークに接続
  • 外部IPを許可: 有効
  • LocalOAuthの認証: 無効
  • Originの有効化: 無効

DeviceWebAPIを起動し、Managerをオフにします。
Managerがオンの場合には、設定を変更できません。

設定、HostにIPアドレスが割り振られている事を確認します。
開発用のPCと同一ネットワークで、IPアドレスベースで接続できる環境である事を確認します。

外部IPを許可を有効にします。
この外部IPを許可を有効にしますと、同じLAN内から、DeviceWebAPI Managerにアクセスすることができるようになります。
この許可がない場合には、同じ端末内からしかアクセスすることができません。
また、この設定はデフォルトでは無効になっています。

Local OAuthOrigin有効化を無効にします。
ユーザ認可を無効にしますので、ユーザの許可なくDeviceWebAPI Managerにアクセスすることができるようになります。
また、この設定はデフォルトでは有効になっています。

Managerをオンにします。

それでは、curlコマンドを用いて開発用のPCから操作してみます。

まずは、DeviceWebAPI ManagerのGotAPIが利用可能な状態かの確認です。IPアドレスは、自分の環境に合わせて書き直してください。

$ curl http://192.168.9.203:4035/gotapi/availability

返り値

{
    "result":0,
    "product":"Device Web API Manager",
    "version":"1.0.6"
}

もし、返り値がJSONで返ってこないこない場合は、Managerへの接続に失敗している可能性があります。Managerがオンになっているか、DeviceWebAPI Managerのインストールされている端末と開発用PCが同一ネットワークにあるかを確認します。

次は、利用できるサービス一覧を表示しましす。

$ curl http://192.168.9.203:4035/gotapi/servicediscovery

返り値

{
    "result":0,
    "product":"Device Web API Manager",
    "version":"1.0.6",
    "services":[
        {
            "online":true,
            "id":"example_service_id.49dfcf44e3d27bd9839abf84373a3d3e.localhost.deviceconnect.org",
            "name":"Device Plugin Sample1",
            "type":"WiFi"
        }
    ]
}

ここで、今回作成中のDeviceプラグインの名前である"name":"Device Plugin Sample1"が表れます。デバイスプラグインへの接続にはserviceIdが必要です。 serviceIdは、idの項目で表れます。"id":"example_service_id.49dfcf44e3d27bd9839abf84373a3d3e.localhost.deviceconnect.org"。以後の接続には、このidを用います。

それでは、今回作成したServiceInformationプロファイルと、Systemプロファイルにアクセスします。

ServiceInformationプロファイルへの接続

$ curl http://192.168.9.203:4035/gotapi/serviceinformation?serviceId=example_service_id.49dfcf44e3d27bd9839abf84373a3d3e.localhost.deviceconnect.org

デバイスプラグインの安全性を確認する認証画面がでますので、同意するを押してください。それによって、/serviceinformationが実行されます。

返り値

{
    "supports":[
        "system","serviceinformation","authorization","servicediscovery"
    ],
    "result":0,
    "product":"Device Web API Manager",
    "version":"1.0.6",
    "connect":{}
}

Systemプロファイルへの接続

curl http://192.168.9.203:4035/gotapi/system?serviceId=example_service_id.49dfcf44e3d27bd9839abf84373a3d3e.localhost.deviceconnect.org

返り値

{
    "supports":[
        "system","files","availability","authorization","servicediscovery"
    ],
    "result":0,
    "plugins":[
        {
            "supports":["servicediscovery","system","serviceinformation"],
            "id":"49dfcf44e3d27bd9839abf84373a3d3e.localhost.deviceconnect.org",
            "name":"DevicePluginSample1",
            "packageName":"com.mycompany.devicepluginsample1"
        }
    ]
}

ここで取れるIDがPluginIDになります。PluginIDとServiceIDが存在している理由としては、1つのDeviceプラグインが複数のServiceを保持している可能性があるためです。

種類
PluginID 49dfcf44e3d27bd9839abf84373a3d3e.localhost.deviceconnect.org
ServiceID example_service_id.49dfcf44e3d27bd9839abf84373a3d3e.localhost.deviceconnect.org

ServiceIDは、PluginIDにDeviceプラグインで指定したIDが追加されたものになります。

Systemプロファイルを用いて設定画面を表示

curl http://192.168.9.203:4035/gotapi//system/device/wakeup?pluginId=49dfcf44e3d27bd9839abf84373a3d3e.localhost.deviceconnect.org

返り値

{"result":0}

設定画面が表示されます。

ServiceDiscoveryプロファイルとSystemプロファイルが作成できたので、ここからは、独自プロファイルの実装方法を解説します。
独自プロファイルは、DConnectProfileを継承する事で開発する事が可能です。
ここでは、ExamplePressureProfile.javaを作成します。

サンプルコード

ExamplePressureProfile.java

package com.mycompany.devicepluginsample1;

import android.content.Intent;
import android.util.Log;
import org.deviceconnect.android.profile.DConnectProfile;
import org.deviceconnect.message.DConnectMessage;

public class ExamplePressureProfile extends DConnectProfile {

    /**
     * profile name: {@value} .
     */
    public final static String PROFILE_NAME = "pressure";

    private final static String TAG = "DEVICE PLUGIN EXAMPLE";

    @Override
    public String getProfileName() {
        return PROFILE_NAME;
    }

    @Override
    protected boolean onGetRequest(final Intent request, final Intent response) {

        String mInterface = getInterface(request);
        String mAttribute = getAttribute(request);
        String mServiceId = getServiceID(request);

        String mParam1 = request.getStringExtra("param1");
        String mParam2 = request.getStringExtra("param2");

        Log.i(TAG, "GET:interface:" + mInterface);
        Log.i(TAG, "GET:attribute:" + mAttribute);
        Log.i(TAG, "GET:serviceId:" + mServiceId);
        Log.i(TAG, "GET:param1:" + mParam1);
        Log.i(TAG, "GET:param2:" + mParam2);

        setResult(response, DConnectMessage.RESULT_OK);
        return true;
    }

    @Override
    protected boolean onPostRequest(final Intent request, final Intent response) {

        String mInterface = getInterface(request);
        String mAttribute = getAttribute(request);
        String mServiceId = getServiceID(request);

        String mParam1 = request.getStringExtra("param1");
        String mParam2 = request.getStringExtra("param2");

        Log.i(TAG, "POST:interface:" + mInterface);
        Log.i(TAG, "POST:attribute:" + mAttribute);
        Log.i(TAG, "POST:serviceId:" + mServiceId);
        Log.i(TAG, "POST:param1:" + mParam1);
        Log.i(TAG, "POST:param2:" + mParam2);

        return true;
    }

    @Override
    protected boolean onPutRequest(final Intent request, final Intent response) {

        String mInterface = getInterface(request);
        String mAttribute = getAttribute(request);
        String mServiceId = getServiceID(request);

        String mParam1 = request.getStringExtra("param1");
        String mParam2 = request.getStringExtra("param2");

        Log.i(TAG, "PUT:interface:" + mInterface);
        Log.i(TAG, "PUT:attribute:" + mAttribute);
        Log.i(TAG, "PUT:serviceId:" + mServiceId);
        Log.i(TAG, "PUT:param1:" + mParam1);
        Log.i(TAG, "PUT:param2:" + mParam2);

        return true;
    }

    @Override
    protected boolean onDeleteRequest(final Intent request, final Intent response) {

        String mInterface = getInterface(request);
        String mAttribute = getAttribute(request);
        String mServiceId = getServiceID(request);

        String mParam1 = request.getStringExtra("param1");
        String mParam2 = request.getStringExtra("param2");

        Log.i(TAG, "DELETE:interface:" + mInterface);
        Log.i(TAG, "DELETE:attribute:" + mAttribute);
        Log.i(TAG, "DELETE:serviceId:" + mServiceId);
        Log.i(TAG, "DELETE:param1:" + mParam1);
        Log.i(TAG, "DELETE:param2:" + mParam2);

        return true;
    }
}

setResult(response, DConnectMessage.RESULT_OK);を設定することで、リクエストの成功を設定します。この設定を行わない場合には失敗が返却されますので、ご注意ください。 また、各エラーコードを返却する場合には、簡易的に設定できるうようにMessageUtilsクラスが用意してありますので、そちらを使用してください。

作成したプロファイルは、Device Connect Managerからも認識できるようにするために、DConnectMessageServiceを継承したExampleDeviceService.javaでaddProfile()で登録し、deviceplugin.xmlにプロファイル名を追記します。

ExampleDeviceService.java

package com.mycompany.devicepluginsample1;

import org.deviceconnect.android.message.DConnectMessageService;
import org.deviceconnect.android.profile.ServiceDiscoveryProfile;
import org.deviceconnect.android.profile.ServiceInformationProfile;
import org.deviceconnect.android.profile.SystemProfile;

/**
 * サンプルのdConnectメッセージサービス実装クラス.
 * @author docomo
 */
public class ExampleDeviceService extends DConnectMessageService {
    @Override
    public void onCreate() {
        super.onCreate();
        // ここで、DConnectMessageServiceにPressureプロファイルを追加します
        addProfile(new ExamplePressureProfile());
    }

    @Override
    protected SystemProfile getSystemProfile() {
        return new ExampleSystemProfile();
    }

    @Override
    protected ServiceInformationProfile getServiceInformationProfile() {
        return new ServiceInformationProfile(this){};
    }

    @Override
    protected ServiceDiscoveryProfile getServiceDiscoveryProfile() {
        return new ExampleServiceDiscoveryProfile(this);
    }
}

res/xml/deviceplugin.xml

<?xml version="1.0" encoding="UTF-8"?>
<deviceplugin-provider xmlns:android="http://schemas.android.com/apk/res/android">
    <profile name="servicediscovery" />
    <profile name="serviceinformation" />
    <profile name="system" />
    <!-- ここで、プロファイルを追加します。 -->
    <profile name="pressure" />
</deviceplugin-provider>

DeviceWebAPI Managerがインストールされている実機にインストールします。デバイスプラグイン管理>DevicePluginSample1 を選択し、pressureがプロファイルとして追加されれば、これで、RESTFulでDeviceプラグインのpressureがよびだせるようになります。

それでは、早速アクセスしてみます。まず、servicediscoveryでserviceIdを取得します。

$ curl http://192.168.9.203:4035/gotapi/servicediscovery

返り値

{
    "result":0,
    "product":"Device Web API Manager",
    "version":"1.0.6",
    "services":[
        {
            "online":true,
            "id":"example_service_id.49dfcf44e3d27bd9839abf84373a3d3e.localhost.deviceconnect.org",
            "name":"Example Device",
            "type":"WiFi"
        }
    ]
}

serviceIdを用いて、pressureにアクセスします。

curl http://192.168.9.203:4035/gotapi/pressure?serviceId=example_service_id.49dfcf44e3d27bd9839abf84373a3d3e.localhost.deviceconnect.org

返り値

{
    "result":0,
    "product":"Device Web API Manager",
    "version":"1.0.6"
}

それでは、実際の気圧の値を取得し、値を返すプログラムを組み込みます。

ExamplePressureProfile.java

package com.mycompany.devicepluginsample1;

import android.content.Context;
import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.util.Log;

import org.deviceconnect.android.message.MessageUtils;
import org.deviceconnect.android.profile.DConnectProfile;
import org.deviceconnect.message.DConnectMessage;

import java.util.List;

public class ExamplePressureProfile extends DConnectProfile {
    /**
     * デバック用のタグ.
     */
    private final static String TAG = "DEVICE PLUGIN EXAMPLE";

    /**
     * profile name: {@value} .
     */
    public  final static String PROFILE_NAME = "pressure";

    /** SensorManagerのインスタンス. */
    private SensorManager mSensorManager = null;

    @Override
    public String getProfileName() {
        return PROFILE_NAME;
    }

    @Override
    protected boolean onGetRequest(final Intent request, final Intent response) {

        String mInterface = getInterface(request);
        String mAttribute = getAttribute(request);
        String mServiceId = getServiceID(request);

        String mParam1 = request.getStringExtra("param1");
        String mParam2 = request.getStringExtra("param2");

        Log.i(TAG, "GET:interface:" + mInterface);
        Log.i(TAG, "GET:attribute:" + mAttribute);
        Log.i(TAG, "GET:serviceId:" + mServiceId);
        Log.i(TAG, "GET:param1:" + mParam1);
        Log.i(TAG, "GET:param2:" + mParam2);

        List<Sensor> sensors;
        final SensorEventListener l = new SensorEventListener() {

            @Override
            public void onSensorChanged(final SensorEvent event) {
                if (event.sensor.getType() == Sensor.TYPE_PRESSURE) {
                    response.putExtra("pressure", event.values[0]);
                    setResult(response, DConnectMessage.RESULT_OK);
                    ExampleDeviceService service = (ExampleDeviceService) getContext();
                    service.sendResponse(response);
                    mSensorManager.unregisterListener(this);
                }
            }

            @Override
            public void onAccuracyChanged(final Sensor sensor, final int accuracy) {
                // No operation
            }
        };

        mSensorManager =  (SensorManager)getContext().getSystemService(Context.SENSOR_SERVICE);
        sensors = mSensorManager.getSensorList(Sensor.TYPE_PRESSURE);

        if (sensors.size() > 0) {
            Sensor sensor = sensors.get(0);
            mSensorManager.registerListener(l, sensor, SensorManager.SENSOR_DELAY_FASTEST);
            // 非同期でレスポンスを返却する場合にはfalseを指定します。
            return false;
        } else {
            MessageUtils.setNotSupportAttributeError(response);
            return true;
        }
    }

    @Override
    protected boolean onPostRequest(final Intent request, final Intent response) {

        String mInterface = getInterface(request);
        String mAttribute = getAttribute(request);
        String mServiceId = getServiceID(request);

        String mParam1 = request.getStringExtra("param1");
        String mParam2 = request.getStringExtra("param2");

        Log.i(TAG, "POST:interface:" + mInterface);
        Log.i(TAG, "POST:attribute:" + mAttribute);
        Log.i(TAG, "POST:serviceId:" + mServiceId);
        Log.i(TAG, "POST:param1:" + mParam1);
        Log.i(TAG, "POST:param2:" + mParam2);

        response.putExtra("pressure", 999);
        response.putExtra("method", "post");

        return true;
    }

    @Override
    protected boolean onPutRequest(final Intent request, final Intent response) {

        String mInterface = getInterface(request);
        String mAttribute = getAttribute(request);
        String mServiceId = getServiceID(request);

        String mParam1 = request.getStringExtra("param1");
        String mParam2 = request.getStringExtra("param2");

        Log.i(TAG, "PUT:interface:" + mInterface);
        Log.i(TAG, "PUT:attribute:" + mAttribute);
        Log.i(TAG, "PUT:serviceId:" + mServiceId);
        Log.i(TAG, "PUT:param1:" + mParam1);
        Log.i(TAG, "PUT:param2:" + mParam2);

        response.putExtra("pressure", 999);
        response.putExtra("method", "put");

        setResult(response, DConnectMessage.RESULT_OK);

        return true;
    }

    @Override
    protected boolean onDeleteRequest(final Intent request, final Intent response) {

        String mInterface = getInterface(request);
        String mAttribute = getAttribute(request);
        String mServiceId = getServiceID(request);

        String mParam1 = request.getStringExtra("param1");
        String mParam2 = request.getStringExtra("param2");

        Log.i(TAG, "DELETE:interface:" + mInterface);
        Log.i(TAG, "DELETE:attribute:" + mAttribute);
        Log.i(TAG, "DELETE:serviceId:" + mServiceId);
        Log.i(TAG, "DELETE:param1:" + mParam1);
        Log.i(TAG, "DELETE:param2:" + mParam2);

        response.putExtra("pressure", 999);
        response.putExtra("method", "delete");

        setResult(response, DConnectMessage.RESULT_OK);

        return true;
    }
}
$ curl http://192.168.9.203:4035/gotapi/pressure?serviceId=ice_id.49dfcf44e3d27bd9839abf84373a3d3e.localhost.deviceconnect.org

返り値

{
    "result":0,
    "version":"1.0.6",
    "product":"Device Web API Manager",
    "pressure":996.6821899414063
}

レスポンスに関するメモ

レスポンスにパラメータを追加したい場合には、Intent#putExtra(String, Object)を行います。
ここでは、response.putExtra("pressure", 999);を追加した場合には、レスポンスのJSONに、"pressure":999が追加されます。
オブジェクトを追加したい場合には、Bundleを追加することで可能です。

Bundle pressure = new Bundle();
pressure.put("value", 999);

response.putExtra("pressure", pressure);

Java側で上記のように実装した場合には、JSONは以下のように取得することができます。

{
    "result":0,
    "version":"1.0.6",
    "product":"Device Web API Manager",
    "pressure": {
        "value": 999
    }
}

また、ExamplePressureProfile#onGetRequest実装では、返り値にfalseを指定しています。 返り値にfalseを指定した場合には、非同期でレスポンスを返却することをDConnectMessageServiceに通知します。そのために、falseを返却した場合に自前で、Device Connect Managerにレスポンスを返却する必要があります。
その時に使用するのがDConnectMessageService#sendResponse(Intent)になります。この関数を呼び出すことでレスポンスを返却することができます。 ただし、返り値にtrueをして、なおかつDConnectMessageService#sendResponse(Intent)を行ってしまうと2重にレスポンスを返却してしまうことになりますので、注意してください。

DConnectMessageServiceは、各DConnectProfile#getContext()で取得することができます。

DConnectMessageService service = (DConnectMessageService) getContext();

それでは、続いてAmbientプロファイルを独自に実装してみます。Ambientは、スマフォ搭載の照度センサーの値を取得します。 照度センサーの値は、明るさに応じて変化するので、イベント処理を実装し、値を返します。

サンプルコード

package com.mycompany.devicepluginsample1;

import android.content.Context;
import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.util.Log;

import org.deviceconnect.android.event.Event;
import org.deviceconnect.android.event.EventError;
import org.deviceconnect.android.event.EventManager;
import org.deviceconnect.android.message.MessageUtils;
import org.deviceconnect.android.profile.DConnectProfile;
import org.deviceconnect.message.DConnectMessage;

import java.util.List;

public class ExampleAmbientProfile extends DConnectProfile {
    /**
     * デバック用のタグ.
     */
    private final static String TAG = "DEVICE PLUGIN EXAMPLE";

    /**
     * profile name: {@value} .
     */
    public  final static String PROFILE_NAME = "ambient";

    /** SensorManagerのインスタンス. */
    private SensorManager mSensorManager = null;

    @Override
    public String getProfileName() {
        return PROFILE_NAME;
    }

    @Override
    protected boolean onGetRequest(final Intent request, final Intent response) {
        List<Sensor> sensors;
        final SensorEventListener l = new SensorEventListener() {
            @Override
            public void onSensorChanged(final SensorEvent event) {
                if (event.sensor.getType() == Sensor.TYPE_LIGHT) {
                    response.putExtra("ambient", event.values[0]);
                    setResult(response, DConnectMessage.RESULT_OK);
                    ExampleDeviceService service = (ExampleDeviceService) getContext();
                    service.sendResponse(response);
                    mSensorManager.unregisterListener(this);
                }
            }

            @Override
            public void onAccuracyChanged(final Sensor sensor, final int accuracy) {
                // No operation
            }
        };

        mSensorManager = (SensorManager)getContext().getSystemService(Context.SENSOR_SERVICE);
        sensors = mSensorManager.getSensorList(Sensor.TYPE_LIGHT);

        if (sensors.size() > 0) {
            Sensor sensor = sensors.get(0);
            mSensorManager.registerListener(l, sensor, SensorManager.SENSOR_DELAY_FASTEST);
            return false;
        } else {
            MessageUtils.setNotSupportAttributeError(response);
            return true;
        }
    }

    @Override
    protected boolean onPostRequest(final Intent request, final Intent response) {
        return true;
    }

    @Override
    protected boolean onPutRequest(final Intent request, final Intent response) {
        final String serviceId = getServiceID(request);
        Log.i(TAG,"PUT!!!!");
        EventError error = EventManager.INSTANCE.addEvent(request);
        if (error == EventError.NONE) {
            setResult(response, DConnectMessage.RESULT_OK);
            List<Sensor> sensors;
            final SensorEventListener l = new SensorEventListener() {
                @Override
                public void onSensorChanged(final SensorEvent event) {
                    if (event.sensor.getType() == Sensor.TYPE_LIGHT) {
                        ExampleDeviceService service = (ExampleDeviceService) getContext();
                        List<Event> events = EventManager.INSTANCE.getEventList(serviceId,
                                getProfileName(), null, "onambient");
                        synchronized (events) {
                            for (Event mEvent : events) {
                                Intent intent = EventManager.createEventMessage(mEvent);
                                intent.putExtra("ambient", event.values[0]);
                                service.sendEvent(intent, mEvent.getAccessToken());
                            }
                        }
                    }
                }

                @Override
                public void onAccuracyChanged(final Sensor sensor, final int accuracy) {
                    // No operation
                }
            };

            mSensorManager =  (SensorManager)getContext().getSystemService(Context.SENSOR_SERVICE);
            sensors = mSensorManager.getSensorList(Sensor.TYPE_LIGHT);

            if (sensors.size() > 0) {
                Sensor sensor = sensors.get(0);
                mSensorManager.registerListener(l, sensor, SensorManager.SENSOR_DELAY_FASTEST);
            } else {
                MessageUtils.setNotSupportAttributeError(response);
            }
        } else {
            MessageUtils.setUnknownError(response);
        }
        return true;
    }

    @Override
    protected boolean onDeleteRequest(final Intent request, final Intent response) {
        EventError error = EventManager.INSTANCE.removeEvent(request);
        if (error == EventError.NONE) {
        } else if (error == EventError.INVALID_PARAMETER) {
            MessageUtils.setInvalidRequestParameterError(response);
        } else if (error == EventError.FAILED) {
            MessageUtils.setUnknownError(response, "Failed to delete event.");
        } else if (error == EventError.NOT_FOUND) {
            MessageUtils.setUnknownError(response, "Not found event.");
        } else {
            MessageUtils.setUnknownError(response);
        }
        return true;
    }
}

また、deviceplugin.xmlにambientプロファイルを追加します。

res/xml/deviceplugin.xml

<?xml version="1.0" encoding="UTF-8"?>
<deviceplugin-provider xmlns:android="http://schemas.android.com/apk/res/android">
    <profile name="servicediscovery" />
    <profile name="serviceinformation" />
    <profile name="system" />
    <!-- ここで、プロファイルを追加します。 -->
    <profile name="pressure" />
    <profile name="ambient" />
</deviceplugin-provider>

イベントの管理には、EventManagerというユーティリティークラスを利用して実装します。

イベントを管理するためのEventManagerの初期化を行います。

public class ExampleDeviceService extends DConnectMessageService {
    @Override
    public void onCreate() {
        super.onCreate();
        // イベントの管理クラスの初期化
        EventManager.INSTANCE.setController(new MemoryCacheController());
        addProfile(new ExampleDeviceOrientationProfile());
    }
    // ・・・省略・・・
}

PUTコマンドで送られてきた時にイベントを登録します。
イベント登録用のonPutXXXX()のメソッドを実装し、EventManager#addEventでイベントを登録することができます。

イベント用のメソッドについては、 Device Connect Android Intent API Specificationから各プロファイル仕様を確認してください。

イベント登録サンプルコード

@Override
protected boolean onPutOnDeviceOrientation(Intent request, Intent response, 
        String serviceId, String sessionKey) {
    EventError error = EventManager.INSTANCE.addEvent(request);
    if (error == EventError.NONE) {
        setResult(response, DConnectMessage.RESULT_OK);
    } else {
        MessageUtils.setUnknownError(response);
    }
    return true;
}

登録されているイベントが発生した時には、DConnectMessageService#sendEventを用いてイベントを送信します。

EventManager#getEventListを用いて、登録されているイベントの一覧を取得し、それぞれの宛先にイベントを送信します。

登録されているイベントは、 DELETEコマンドが送られてきた時に解除します。
イベント停止用のonDeleteXXXX()のメソッドを実装し、EventManager#removeEventでイベントを解除することができます。

また、すべてのイベントを解除するSystem Events Unregister APIがあります。
このAPIが呼び出された場合には、登録されているイベントをすべて解除する必要があります。

イベント解除サンプルコード

@Override
protected boolean onDeleteOnDeviceOrientation(Intent request, Intent response, 
        String serviceId, String sessionKey) {
    EventError error = EventManager.INSTANCE.removeEvent(request);
    if (error == EventError.NONE) {
        setResult(response, DConnectMessage.RESULT_OK);
    } else {
        MessageUtils.setUnknownError(response);
    }
    return true;
}

以下のサンプルでは定期的に加速度センサーのイベントを通知します。

ExampleDeviceService.java

package com.mycompany.devicepluginsample1;

import org.deviceconnect.android.event.EventManager;
import org.deviceconnect.android.event.cache.MemoryCacheController;
import org.deviceconnect.android.message.DConnectMessageService;
import org.deviceconnect.android.profile.ServiceDiscoveryProfile;
import org.deviceconnect.android.profile.ServiceInformationProfile;
import org.deviceconnect.android.profile.SystemProfile;

/**
 * サンプルのdConnectメッセージサービス実装クラス.
 * @author docomo
 */
public class ExampleDeviceService extends DConnectMessageService {
    @Override
    public void onCreate() {
        super.onCreate();
        // イベントの管理クラスの初期化
        EventManager.INSTANCE.setController(new MemoryCacheController());
        // プロファイルの追加
        addProfile(new ExampleDeviceOrientationProfile());
    }

    @Override
    protected SystemProfile getSystemProfile() {
        return new ExampleSystemProfile();
    }

    @Override
    protected ServiceInformationProfile getServiceInformationProfile() {
        return new ServiceInformationProfile(this) {};
    }

    @Override
    protected ServiceDiscoveryProfile getServiceDiscoveryProfile() {
        return new ExampleServiceDiscoveryProfile(this);
    }
}

ExampleDeviceOrientationProfile.java

public class ExampleDeviceOrientationProfile extends DeviceOrientationProfile {
    private ScheduledExecutorService mExecutor = Executors.newSingleThreadScheduledExecutor();

    public ExampleDeviceOrientationProfile() {
        // 2秒毎にイベントを発生させるためのスケジュールを登録
        mExecutor.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                notifyDeviceOrientation("example_service_id");
            }
        }, 2, 2, TimeUnit.SECONDS);
    }

    @Override
    protected boolean onPutOnDeviceOrientation(Intent request, Intent response, String serviceId,
            String sessionKey) {
        EventError error = EventManager.INSTANCE.addEvent(request);
        if (error == EventError.NONE) {
            setResult(response, DConnectMessage.RESULT_OK);
        } else {
            MessageUtils.setUnknownError(response);
        }
        return true;
    }

    @Override
    protected boolean onDeleteOnDeviceOrientation(Intent request, Intent response, String serviceId,
            String sessionKey) {
        EventError error = EventManager.INSTANCE.removeEvent(request);
        if (error == EventError.NONE) {
            setResult(response, DConnectMessage.RESULT_OK);
        } else if (error == EventError.INVALID_PARAMETER) {
            MessageUtils.setInvalidRequestParameterError(response);
        } else if (error == EventError.FAILED) {
            MessageUtils.setUnknownError(response, "Failed to delete event.");
        } else if (error == EventError.NOT_FOUND) {
            MessageUtils.setUnknownError(response, "Not found event.");
        } else {
            MessageUtils.setUnknownError(response);
        }
        return true;
    }

    /**
     * サンプル用にメッセージを送信する。
     * @param serviceId イベントを発生するサービスID
     */
    private void notifyDeviceOrientation(String serviceId) {
        ExampleDeviceService service = (ExampleDeviceService) getContext();
        List<Event> events = EventManager.INSTANCE.getEventList(serviceId,
           getProfileName(), null, ATTRIBUTE_ON_DEVICE_ORIENTATION);
        synchronized (events) {
            for (Event event : events) {
                Intent intent = EventManager.createEventMessage(event);
                setOrientation(intent);
                service.sendEvent(intent, event.getAccessToken());
            }
        }
    }

    /**
     * メッセージにサンプルデータを設定する.
     * @param message メッセージ
     */
    private void setOrientation(final Intent message) {
        Bundle orientation = new Bundle();
        Bundle a1 = new Bundle();
        setX(a1, 0.0);
        setY(a1, 0.0);
        setZ(a1, 0.0);
        setAcceleration(orientation, a1);

        Bundle a2 = new Bundle();
        setX(a2, 0.0);
        setY(a2, 0.0);
        setZ(a2, 0.0);
        setAccelerationIncludingGravity(orientation, a2);

        Bundle r = new Bundle();
        setAlpha(r, 0.0);
        setBeta(r, 0.0);
        setGamma(r, 0.0);
        setRotationRate(orientation, r);

        setOrientation(message, orientation);
        setInterval(orientation, 0);
    }
}
  1. Manager・プラグイン間通信フォーマット
  2. 開発マニュアル
  3. ビルドマニュアル
  4. ユーザーマニュアル

Clone this wiki locally