Application Manual for Android Studio

TakayukiHoshi1984 edited this page Jul 18, 2018 · 3 revisions

目次

1. はじめに

本ドキュメントは、Device Connect APIを用いてAndroidネイティブアプリケーションを開発する手法について解説します。Androidに関する一般的な知識と、Javaに関する一般的な知識を保持している事を提に解説していきます。
本ドキュメントでは、開発環境としてAndroid Studioを使用します。

Android Studioは、こちらからダウンロードを行ってください。

2. Device Connect について

Device Connectでは、RESTfulで、命令やデータの送受信が可能です。各処理は、Profileという形でURIが割り振られ、そのURIをJavaやJavaScriptなどで呼び出す事で、処理を行います。

2.1. Device Connect対応のProfile

Device Connectで、使用出来るProfileを以下に示します。

Profile名 説明
System Device Connect本体の情報や、プラグインの情報、プラグインの設定画面の起動などを行います。
Battery バッテリーの状態を保持します
Proximity デバイスと物体との近接状態を取得します。
Media Player メディアの再生(音声, 音楽, 動画)を行います。
Service Discovery デバイスの対応Profile一覧を取得します。
Vibration デバイスをバイブレートする命令を送ります。
Notification デバイスに通知(Notification)を行います。
Setting デバイスの設定(音量, 画面輝度, 画面スリープ時間等)を行います。
DeviceOrientation デバイスの加速度等を取得します。
File デバイスとFileの送信、受信等を行います。
MediaStream Recording API メディアの録音(音声,音楽,動画)を行います。
Phone API デバイスに電話発信の命令を送ります。

ただし、デバイスによって、使用出来るProfileは変わりますので、ご注意ください。

2.2. Device Connectへの接続

Android版Device Connectでは、端末内のローカルに起動しているHTTPサーバ(DeviceConnectManager)に問い合わせを行う事で、ハードウェアの操作や、情報取得を行う事が可能です。

図1. Device Connect Managerへの問い合わせ

2.3 Device Connectの準備

2.3.1 DeviceConnect-Androidの取得

githubのDownload ZIPボタンを押下して、ダウンロードしてください。

もしくは、gitコマンドを用いてプロジェクトをチェックアウトしてください。

$ git clone https://github.com/DeviceConnect/DeviceConnect-Android.git

2.3.1 Device Connect Managerのインストール

Android Studioで/DeviceConnect-Android/dConnectManager/dConnectManagerを開きます。

appを実行することで、Device Connect Managerがインストールされます。

2.3.2 Device Connect Managerの起動

Device Connect Managerのアイコンを押下して、アプリを起動します。
Device Connect Managerのスイッチを押下して、オンにします。

2.3.3 HOSTデバイスプラグインの確認

Device Connect Manager [デバイスプラグイン>デバイスプラグイン管理]を選択します。

リストに HOST (Device Connect Device Plug-in) が追加されていることを確認します。

以上で、Device Connectを使用するための準備が整いました。
次からサンプルの作成を説明します。

3. サンプルアプリケーションの作成

3.1. プロジェクトの作成

サンプルアプリケーションでは、Device Connect Managerに問い合わせを行い、結果のJSONを取得するサンプルを作成しながら解説をします。

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

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

項目 設定値
Application Name AndroidAppSample
Project Name AndroidAppSample
Package Name com.example.androidappsample
Minimum Required SDK Android 4.2

Activityを選択します。

Activity名を入力します。

ここまでCreate New Projectに従ってプロジェクトを作成すると、以下のようにプロジェクトが追加されます。

使用するAndroid StudioのバージョンやAndroid SDKのバージョンによっては、生成されるソースコードやxmlに違いがありますので、ご注意ください。

3.2. MainActivityの編集

表示画面となる res/layout/activity_main.xmlと、MainActivity.javaを編集します。

res/layout/activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="${relativePackage}.${activityClass}" >

    <Button android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Button" />

</RelativeLayout>

MainActivity.java

package com.example.androidappsample;

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.os.Looper;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity implements OnClickListener {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		Button button = (Button) findViewById(R.id.button);
		button.setOnClickListener(this);
	}

	@Override
	public void onClick(View v) {
		(new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					String url = "http://localhost:4035/gotapi/availability";
					HttpGet request = new HttpGet(url);
					request.addHeader("origin", getPackageName());
					HttpClient client = new DefaultHttpClient();
					HttpResponse response = client.execute(request);
					String result = EntityUtils.toString(response.getEntity(), "UTF-8");
					Looper.prepare();
					Toast.makeText(getApplicationContext(), result, Toast.LENGTH_LONG).show();
					Looper.loop();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		})).start();
	}
}

HTTP通信を行うため、AndroidManifest.xmlに、INTERNETのPermissionを追加してください。

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.androidappsample"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="21" />

    <uses-permission android:name="android.permission.INTERNET"/>

    <application
        android:allowBackup="true"
        android:icon="@drawable/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>
    </application>

</manifest>

ここまでの作成を終えた後、このサンプルをインストールして起動すると、以下のような画面が表示されます。

Buttonを押す事で、端末にインストールされているDevice Connect 1.0 デバイスプラグインの情報を取得して、Toastで表示する事が出来ます。

4. SDKを用いたAndroidアプリケーションの開発

Device Connectとの通信には、いくつかの手順を行う必要があります。Androidでアプリケーションを開発する際に、最初にAccessTokenを取得します。また、接続したハードウェアのServiceIdをService Discovery Profileにアクセスして取得します。AccessTokenとServiceIdを用いて、操作したいハードウェアのプラグインに対して処理の送信が可能となります。

4.1. AccessTokenをDevice Connectから取得

Device Connectにアクセスするアプリは、OAuth(Local)認証で用いるAccessTokenを、Device Connect Managerより発行してもらう必要があります。本サンプルから、Device Connect SDK Android版を用いて解説します。

4.1.1. dConnectSDKAndroidのインポート

AndroidStudioのGradle Scriptの欄から「build.gradle」を開き以下のような内容を追記します。

build.gradle

 apply plugin: 'com.android.application'

android {
    compileSdkVersion 21
    buildToolsVersion "21.1.1"

    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'
    }
    defaultConfig {
        applicationId "com.example.androidappsample"
        minSdkVersion 17
        targetSdkVersion 21
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

repositories {
    maven { url 'http://raw.github.com/DeviceConnect/DeviceConnect-Android/master/dConnectSDK/dConnectSDKForAndroid/repository/' }
}

configurations {
    all*.exclude group: 'org.apache.httpcomponents', module: 'httpclient'
    all*.exclude group: 'commons-logging', module: 'commons-logging'
    all*.exclude group: 'com.android.support', module: 'support-v4'
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:21.0.3'
    compile 'org.deviceconnect:dconnect-sdk-for-android:1.0.1'
}

repositoriesタグにより、GitHubからSDKをダウンロードすることができます。 configurationタグによって、support-v4ライブラリの重複を防ぐための処理を追加しています。
GradleScriptを編集すると、以下の図のようにGradleをビルドするかどうかを尋ねられるので、「Sync Now」でGradleをビルドします。

GradleScriptのビルドに成功すると、dconnect-sdk-for-androidがインポートされます。

4.1.2. AccessToken取得部の実装

画面にボタンを追加して、ボタン押下でAccessToken取得処理を行わせるように実装します。

res/layout/activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="${relativePackage}.${activityClass}" >

    <Button android:id="@+id/button1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="System" />

    <Button android:id="@+id/button2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/button1"
        android:text="Get AccessToken" />

</RelativeLayout>

MainActivity.java

package com.example.androidappsample;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.os.Looper;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.deviceconnect.message.DConnectMessage.ErrorCode;
import org.deviceconnect.profile.AuthorizationProfileConstants;
import org.deviceconnect.profile.BatteryProfileConstants;
import org.deviceconnect.profile.ConnectProfileConstants;
import org.deviceconnect.profile.DeviceOrientationProfileConstants;
import org.deviceconnect.profile.FileDescriptorProfileConstants;
import org.deviceconnect.profile.FileProfileConstants;
import org.deviceconnect.profile.MediaPlayerProfileConstants;
import org.deviceconnect.profile.MediaStreamRecordingProfileConstants;
import org.deviceconnect.profile.NotificationProfileConstants;
import org.deviceconnect.profile.PhoneProfileConstants;
import org.deviceconnect.profile.ProximityProfileConstants;
import org.deviceconnect.profile.ServiceDiscoveryProfileConstants;
import org.deviceconnect.profile.SettingsProfileConstants;
import org.deviceconnect.profile.SystemProfileConstants;
import org.deviceconnect.profile.VibrationProfileConstants;
import org.deviceconnect.utils.AuthProcesser;

public class MainActivity extends Activity implements OnClickListener {
    private Button mButton1;
    private Button mButton2;
    private Context mContext;

    private String mClientId;
    private String mAccessToken;
    private ErrorCode mError;
    /**
     * Local OAuthに使用するスコープ一覧.
     */
    private String[] scopes = {
 		    ServiceInformationProfileConstants.PROFILE_NAME,
            AuthorizationProfileConstants.PROFILE_NAME,
            BatteryProfileConstants.PROFILE_NAME,
            ConnectionProfileConstants.PROFILE_NAME,
            DeviceOrientationProfileConstants.PROFILE_NAME,
            FileDescriptorProfileConstants.PROFILE_NAME,
            FileProfileConstants.PROFILE_NAME,
            MediaPlayerProfileConstants.PROFILE_NAME,
            MediaStreamRecordingProfileConstants.PROFILE_NAME,
            ServiceDiscoveryProfileConstants.PROFILE_NAME,
            NotificationProfileConstants.PROFILE_NAME,
            PhoneProfileConstants.PROFILE_NAME,
            ProximityProfileConstants.PROFILE_NAME,
            SettingProfileConstants.PROFILE_NAME,
            SystemProfileConstants.PROFILE_NAME,
            VibrationProfileConstants.PROFILE_NAME,
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContext = this.getBaseContext();
        mButton1 = (Button) findViewById(R.id.button1);
        mButton2 = (Button) findViewById(R.id.button2);
        mButton1.setOnClickListener(this);
        mButton2.setOnClickListener(this);
    }
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.button1:
                (new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            String url = "http://localhost:4035/gotapi/availability";
                            HttpGet request = new HttpGet(url);
                            request.addHeader("origin", getPackageName());
                            HttpClient client = new DefaultHttpClient();
                            HttpResponse response = client.execute(request);
                            String result = EntityUtils.toString(response.getEntity(), "UTF-8");
                            Looper.prepare();
                            Toast.makeText(mContext, result, Toast.LENGTH_LONG).show();
                            Looper.loop();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                })).start();
                break;
            case R.id.button2:
                String appName = getResources().getString(R.string.app_name);
                AuthProcesser.asyncAuthorize("localhost", 4035, false, getPackageName(),
                        appName, scopes, mAuthHandler);
                break;
        }
    }
    /**
     * Local OAuth のリスナー. */
    private AuthProcesser.AuthorizationHandler mAuthHandler = new AuthProcesser.AuthorizationHandler() {
        @Override
         public void onAuthorized(final String clientId, 
                                  final String accessToken) {
            mClientId = clientId;
            mAccessToken = accessToken;
            mError = null;
            MainActivity.this.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(MainActivity.this, "Success. AccessToken="
                            + mAccessToken + " ClientId=" + clientId, Toast.LENGTH_LONG).show();
                }
            });
        }
        @Override
        public void onAuthFailed(final ErrorCode error) {
            mError = error;
            mClientId = null;
            mAccessToken = null;
            MainActivity.this.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(MainActivity.this, error.toString(),
                            Toast.LENGTH_LONG).show();
                }
            });
        }
    };
}

ここまでの作成を終えた後、このサンプルをインストールして起動すると、以下のような画面が表示されます。

「Get AccessToken」Buttonを押す事で、AccessToken取得処理が実行され、認証画面が表示されます。

認証を行うと、ToastでAccessTokenが表示されます。

4.2. ServiceDiscoveryの実装

デバイス一覧を取得するためのServiceDiscoveryの実装を行います。 画面にボタンを追加して、ボタン押下でServiceDiscovery処理を行わせるように実装します。 また、Toastで表示していた応答を、EditTextにて表示できるように変更も行います。

res/layout/activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="${relativePackage}.${activityClass}" >

    <Button android:id="@+id/button1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="System" />

    <Button android:id="@+id/button2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/button1"
        android:text="Get AccessToken" />

    <Button android:id="@+id/button3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/button2"
        android:text="Network Service Discovery" />

    <EditText
        android:id="@+id/editText1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:ems="10"
        android:inputType="textMultiLine" >
        
        <requestFocus />
    </EditText>

</RelativeLayout>

MainActivity.java

package com.example.androidappsample;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.os.Looper;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.deviceconnect.message.DConnectMessage;
import org.deviceconnect.message.DConnectMessage.ErrorCode;
import org.deviceconnect.message.basic.message.DConnectResponseMessage;
import org.deviceconnect.message.http.impl.factory.HttpMessageFactory;
import org.deviceconnect.profile.AuthorizationProfileConstants;
import org.deviceconnect.profile.BatteryProfileConstants;
import org.deviceconnect.profile.ConnectProfileConstants;
import org.deviceconnect.profile.DeviceOrientationProfileConstants;
import org.deviceconnect.profile.FileDescriptorProfileConstants;
import org.deviceconnect.profile.FileProfileConstants;
import org.deviceconnect.profile.MediaPlayerProfileConstants;
import org.deviceconnect.profile.MediaStreamRecordingProfileConstants;
import org.deviceconnect.profile.NotificationProfileConstants;
import org.deviceconnect.profile.PhoneProfileConstants;
import org.deviceconnect.profile.ProximityProfileConstants;
import org.deviceconnect.profile.ServiceDiscoveryProfileConstants;
import org.deviceconnect.profile.ServiceInformationProfileConstants;
import org.deviceconnect.profile.SettingsProfileConstants;
import org.deviceconnect.profile.SystemProfileConstants;
import org.deviceconnect.profile.VibrationProfileConstants;
import org.deviceconnect.utils.AuthProcesser;
import org.deviceconnect.utils.URIBuilder;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class MainActivity extends Activity implements OnClickListener {
    private Button mButton1;
    private Button mButton2;
    private Button mButton3;
    private Context mContext;
    private EditText mEditText1;
    private String mClientId;
    private String mAccessToken;
    private ErrorCode mError;
    private List<SmartDevice> devices = null;
    /**
     * Local OAuthに使用するスコープ一覧.
     */
    private String[] scopes = {
            AuthorizationProfileConstants.PROFILE_NAME,
            BatteryProfileConstants.PROFILE_NAME,
            ConnectProfileConstants.PROFILE_NAME,
            DeviceOrientationProfileConstants.PROFILE_NAME,
            FileDescriptorProfileConstants.PROFILE_NAME,
            FileProfileConstants.PROFILE_NAME,
            LightProfileConstants.PROFILE_NAME,
            MediaPlayerProfileConstants.PROFILE_NAME,
            MediaStreamRecordingProfileConstants.PROFILE_NAME,
            ServiceDiscoveryProfileConstants.PROFILE_NAME,
            NotificationProfileConstants.PROFILE_NAME,
            PhoneProfileConstants.PROFILE_NAME,
            ProximityProfileConstants.PROFILE_NAME,
            ServiceInformationProfileConstants.PROFILE_NAME,
            SettingsProfileConstants.PROFILE_NAME,
            SystemProfileConstants.PROFILE_NAME,
            VibrationProfileConstants.PROFILE_NAME,
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContext = this.getBaseContext();
        mEditText1 = (EditText) findViewById(R.id.editText1);
        mButton1 = (Button) findViewById(R.id.button1);
        mButton2 = (Button) findViewById(R.id.button2);
        mButton3 = (Button) findViewById(R.id.button3);
        mButton1.setOnClickListener(this);
        mButton2.setOnClickListener(this);
        mButton3.setOnClickListener(this);
    }
    public void setTextField(String text) {
        mEditText1.setText(text);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.button1:
                (new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            String url = "http://localhost:4035/gotapi/system";
                            HttpGet request = new HttpGet(url);
                            request.addHeader("origin", getPackageName());
                            HttpClient client = new DefaultHttpClient();
                            HttpResponse response = client.execute(request);
                            String result = EntityUtils.toString(response.getEntity(), "UTF-8");
                            Looper.prepare();
                            Toast.makeText(mContext, result, Toast.LENGTH_LONG).show();
                            Looper.loop();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                })).start();
                break;
            case R.id.button2:
                String appName = getResources().getString(R.string.app_name);
                AuthProcesser.asyncAuthorize("localhost", 4035, false, getPackageName(), appName, scopes, mAuthHandler);
                break;
            case R.id.button3:
                (new Thread(new Runnable() {
                    @Override
                    public void run() {
                        DConnectMessage message = new DConnectResponseMessage(DConnectMessage.RESULT_ERROR);
                        devices = new ArrayList<SmartDevice>();
                        try {
                            URIBuilder builder = new URIBuilder();
                            builder.setScheme("http");
                            builder.setProfile(ServiceDiscoveryProfileConstants.PROFILE_NAME);
                            builder.setHost("localhost");
                            builder.setPort(4035);
                            builder.addParameter(DConnectMessage.EXTRA_ACCESS_TOKEN, mAccessToken);
                            HttpUriRequest request = new HttpGet(builder.build());
                            request.addHeader("origin", getPackageName());
                            HttpClient client = new DefaultHttpClient();
                            HttpResponse response = client.execute(request);
                            message = (new HttpMessageFactory()).newDConnectMessage(response);
                        } catch (URISyntaxException e) {
                            e.printStackTrace();
                            return;
                        } catch (IOException e) {
                            e.printStackTrace();
                            return;
                        }
                        if (message == null) {
                            return;
                        }
                        int result = message.getInt(DConnectMessage.EXTRA_RESULT);
                        if (result == DConnectMessage.RESULT_ERROR) {
                            return;
                        }
                        List<Object> services
                                = message.getList(
                                    ServiceDiscoveryProfileConstants.PARAM_SERVICES);
                        final StringBuffer sb = new StringBuffer();
                        if (services != null) {
                            for (Object object: services) {
                                @SuppressWarnings("unchecked")
                                Map<String, Object> service = (Map<String, Object>) object;
                                SmartDevice device = new SmartDevice(
                                        service.get(ServiceDiscoveryProfileConstants.PARAM_ID).toString(),
                                        service.get(ServiceDiscoveryProfileConstants.PARAM_NAME).toString());
                                devices.add(device);
                                sb.append("id:" +
                                        service.get(ServiceDiscoveryProfileConstants.PARAM_ID).toString() + ", ");
                                sb.append("name:" +
                                        service.get(ServiceDiscoveryProfileConstants.PARAM_NAME).toString() + ", "); }
                        }
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                mEditText1.setText(sb);
                            }
                        });
                    }
                })).start();
                break;
        }
    }
    /**
     * Local OAuth のリスナー. */
    private AuthProcesser.AuthorizationHandler mAuthHandler = new AuthProcesser.AuthorizationHandler() {
        @Override
         public void onAuthorized(final String clientId, final String accessToken) {
            mClientId = clientId;
            mAccessToken = accessToken;
            mError = null;
            MainActivity.this.runOnUiThread(new Runnable() { 
            	@Override
                public void run() {
                	Toast.makeText(MainActivity.this,
                		 "Success. AccessToken="
                        + mAccessToken + " ClientId="
                        + clientId, 
                        Toast.LENGTH_LONG).show();
            } });
        }
        @Override
        public void onAuthFailed(final ErrorCode error) {
            mError = error;
            mClientId = null;
            mAccessToken = null;
            MainActivity.this.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(MainActivity.this, error.toString(),
                            Toast.LENGTH_LONG).show();
                }
            });
        }
    };
}

SmartDevice.java

package com.example.androidappsample;
import java.util.ArrayList;
import java.util.List;
import android.os.Parcel; import android.os.Parcelable;
/**
 * Device Connect スマートデバイス. */
public class SmartDevice implements Parcelable {
    /**
     * Parcelable クリエイター. */
    public static final Parcelable.Creator<SmartDevice> CREATOR
            = new Parcelable.Creator<SmartDevice>() {
        public SmartDevice createFromParcel(final Parcel in) {
         return new SmartDevice(in);
        }
        public SmartDevice[] newArray(final int size) {
            return new SmartDevice[size];
        }
    };
    /**
     * デバイス名. */
    private String mName;
    /**
     * デバイス種別. */
    private String mType;
    /**
     * デバイス ID. */
    private String mId;
    /**
     * サービスリスト. */
    private List<SmartService> mServiceList = new ArrayList<SmartService>();
    /**
     * コンストラクタ.
     * @param id デバイス ID * @param name デバイス名 */
    public SmartDevice(final String id, final String name) {
        setId(id);
        setName(name);
    }
    /**
     * Parcelable コンストラクタ.
     * @param in 入力
     */
    private SmartDevice(final Parcel in) {
        setName(in.readString());
        setType(in.readString());
        setId(in.readString());
    }
    @Override
    public int describeContents() {
        return 0;
    }
    @Override
    public void writeToParcel(final Parcel dest, final int flags) {
        dest.writeString(mName);
        dest.writeString(mType);
        dest.writeString(mId);
    }
    @Override
    public String toString() {
        return mName;
    }
    /**
     * デバイス ID を設定する.
     * @param id デバイス ID
     */
    public void setId(final String id) {
        mId = id;
    }
    /**
     * デバイス名を設定する.
     * @param name デバイス名
     */
    public void setName(final String name) {
        mName = name;
    }
    /**
     * デバイス種別を設定する.
     * @param type デバイス種別
     */
    public void setType(final String type) {
        mType = type;
    }
    /**
     * デバイス名.
     * @return デバイス名
     */
    public String getName() {
        return mName;
    }
    /**
     * デバイス種別を取得する.
     * @return デバイス種別
     */
    public String getType() {
        return mType;
    }
    /**
     * デバイス ID を取得する.
     * @return デバイス ID
     */
    public String getId() {
        return mId;
    }
	/**
	 * サービスリストを取得する.
	 * @return サービスリスト
	 */
    public List<SmartService> getmServiceList() {
        return mServiceList;
    }
    /**
     * サービスを追加する.
     * @param service サービス
     */
    public void addService(final SmartService service) {
        mServiceList.add(service);
    }
    /**
     * サービスを削除する.
     * @param service サービス
     */
    public void removeService(final SmartService service) {
        mServiceList.remove(service);
    }
}

SmartService.java

package com.example.androidappsample;

/**
 * スマートサービス(プロファイル).
 */
public class SmartService {

    /**
     * プロファイル名.
     */
    private String mName;

    /**
     * コンストラクタ.
     * @param name プロファイル名
     */
    public SmartService(final String name) {
        mName = name;
    }

    @Override
    public String toString() {
        return mName;
    }

    /**
     * プロファイル名を設定.
     * @param name プロファイル名
     */
    public void setName(final String name) {
        mName = name;
    }

    /**
     * プロファイル名を取得.
     * @return プロファイル名
     */
    public String getName() {
        return mName;
    }

    /**
     * アイコンを取得.
     * @return アイコンID
     */
    public int getIconId() {
        return android.R.drawable.ic_menu_info_details;
    }

}

ここまでの作成を終えた後、このサンプルをインストールして起動すると、以下のような画面が表示されます。

「Service Discovery」Buttonを押す事で、Service Discovery処理が実行され、デバイスの情報が表示されます。

4.3.デバイスプラグインのRESTfulでの問い合わせ処理実装

ここまでの実装で取得した「AccessToken」「ServiceId」を使用して、デバイスプラグインへRESTfulでの問い合わせ処理の実装を行います。 ここでは、画面にボタンを追加して、ボタン押下でService Discoveryで最初に発見されたデバイスプラグインに対して、「/serviceinformation」を実行し、その応答を得る実装を行います。

res/layout/activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="${relativePackage}.${activityClass}" >

    <Button android:id="@+id/button1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="System" />

    <Button android:id="@+id/button2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/button1"
        android:text="Get AccessToken" />

    <Button android:id="@+id/button3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/button2"
        android:text="Service Discovery" />

    <Button android:id="@+id/button4"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/button3"
        android:text="Device" />

    <EditText
        android:id="@+id/editText1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:ems="10"
        android:inputType="textMultiLine" >

        <requestFocus />
    </EditText>

</RelativeLayout>

MainActivity.java

package com.example.androidappsample;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.os.Looper;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.deviceconnect.message.DConnectMessage;
import org.deviceconnect.message.DConnectMessage.ErrorCode;
import org.deviceconnect.message.basic.message.DConnectResponseMessage;
import org.deviceconnect.message.http.impl.factory.HttpMessageFactory;
import org.deviceconnect.profile.AuthorizationProfileConstants;
import org.deviceconnect.profile.BatteryProfileConstants;
import org.deviceconnect.profile.ConnectProfileConstants;
import org.deviceconnect.profile.DeviceOrientationProfileConstants;
import org.deviceconnect.profile.FileDescriptorProfileConstants;
import org.deviceconnect.profile.FileProfileConstants;
import org.deviceconnect.profile.MediaPlayerProfileConstants;
import org.deviceconnect.profile.MediaStreamRecordingProfileConstants;
import org.deviceconnect.profile.NotificationProfileConstants;
import org.deviceconnect.profile.PhoneProfileConstants;
import org.deviceconnect.profile.ProximityProfileConstants;
import org.deviceconnect.profile.ServiceDiscoveryProfileConstants;
import org.deviceconnect.profile.ServiceInformationProfileConstants;
import org.deviceconnect.profile.SettingsProfileConstants;
import org.deviceconnect.profile.SystemProfileConstants;
import org.deviceconnect.profile.VibrationProfileConstants;
import org.deviceconnect.utils.AuthProcesser;
import org.deviceconnect.utils.URIBuilder;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class MainActivity extends Activity implements OnClickListener {
    private Button mButton1;
    private Button mButton2;
    private Button mButton3;
    private Button mButton4;
    private Context mContext;
    private EditText mEditText1;
    private String mClientId;
    private String mAccessToken;
    private ErrorCode mError;
    private List<SmartDevice> devices = null;
    /**
     * Local OAuthに使用するスコープ一覧.
     */
    private String[] scopes = {
            ServiceInformationProfileConstants.PROFILE_NAME,
            AuthorizationProfileConstants.PROFILE_NAME,
            BatteryProfileConstants.PROFILE_NAME,
            ConnectProfileConstants.PROFILE_NAME,
            DeviceOrientationProfileConstants.PROFILE_NAME,
            FileDescriptorProfileConstants.PROFILE_NAME,
            FileProfileConstants.PROFILE_NAME,
            MediaPlayerProfileConstants.PROFILE_NAME,
            MediaStreamRecordingProfileConstants.PROFILE_NAME,
            ServiceDiscoveryProfileConstants.PROFILE_NAME,
            NotificationProfileConstants.PROFILE_NAME,
            PhoneProfileConstants.PROFILE_NAME,
            ProximityProfileConstants.PROFILE_NAME,
            SettingsProfileConstants.PROFILE_NAME,
            SystemProfileConstants.PROFILE_NAME,
            VibrationProfileConstants.PROFILE_NAME,
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContext = this.getBaseContext();
        mEditText1 = (EditText) findViewById(R.id.editText1);
        mButton1 = (Button) findViewById(R.id.button1);
        mButton2 = (Button) findViewById(R.id.button2);
        mButton3 = (Button) findViewById(R.id.button3);
        mButton4 = (Button) findViewById(R.id.button4);
        mButton1.setOnClickListener(this);
        mButton2.setOnClickListener(this);
        mButton3.setOnClickListener(this);
        mButton4.setOnClickListener(this);
    }
    public void setTextField(String text) {
        mEditText1.setText(text);
    }
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.button1:
                (new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            String url = "http://localhost:4035/gotapi/system";
                            HttpGet reqeust = new HttpGet(url);
                            request.addHeader("origin", getPackageName());
                            HttpClient client = new DefaultHttpClient();
                            HttpResponse response = client.execute(reqeust);
                            String result = EntityUtils.toString(response.getEntity(), "UTF-8");
                            Looper.prepare();
                            Toast.makeText(mContext, result, Toast.LENGTH_LONG).show();
                            Looper.loop();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                })).start();
                break;
            case R.id.button2:
                String appName = getResources().getString(R.string.app_name);
                AuthProcesser.asyncAuthorize("localhost", 4035, false, getPackageName(),
                        appName, scopes, mAuthHandler);
                break;
            case R.id.button3:
                (new Thread(new Runnable() {
                    @Override
                    public void run() {
                        DConnectMessage message = new DConnectResponseMessage(DConnectMessage.RESULT_ERROR);
                        devices = new ArrayList<SmartDevice>();
                        try {
                            URIBuilder builder = new URIBuilder();
                            builder.setScheme("http");
                            builder.setProfile(ServiceDiscoveryProfileConstants.PROFILE_NAME);
                            builder.setHost("localhost");
                            builder.setPort(4035);
                            builder.addParameter(DConnectMessage.EXTRA_ACCESS_TOKEN, mAccessToken);
                            HttpUriRequest request = new HttpGet(builder.build());
                            request.addHeader("origin", getPackageName());
                            DefaultHttpClient client = new DefaultHttpClient();
                            HttpResponse response = client.execute(request);
                            message = (new HttpMessageFactory()).newDConnectMessage(response);
                        } catch (URISyntaxException e) {
                            e.printStackTrace();
                            return;
                        } catch (IOException e) {
                            e.printStackTrace();
                            return;
                        }
                        if (message == null) {
                            return;
                        }
                        int result = message.getInt(DConnectMessage.EXTRA_RESULT);
                        if (result == DConnectMessage.RESULT_ERROR) {
                            return;
                        }
                        List<Object> services
                                = message.getList(
                                ServiceDiscoveryProfileConstants.PARAM_SERVICES);
                        final StringBuffer sb = new StringBuffer();
                        if (services != null) {
                            for (Object object: services) {
                                @SuppressWarnings("unchecked")
                                Map<String, Object> service = (Map<String, Object>) object;
                                SmartDevice device = new SmartDevice(
                                        service.get(ServiceDiscoveryProfileConstants.PARAM_ID).toString(),
                                        service.get(ServiceDiscoveryProfileConstants.PARAM_NAME).toString());
                                devices.add(device);
                                sb.append("id:" +
                                        service.get(ServiceDiscoveryProfileConstants.PARAM_ID).toString()
                                        + ", ");
                                sb.append("name:" +
                                        service.get(ServiceDiscoveryProfileConstants.PARAM_NAME).toString()
                                        + ", "); }
                        }
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                mEditText1.setText(sb);
                            }
                        });
                    }
                })).start();
                break;
            case R.id.button4:
                if (devices.size() == 0) {
                    return;
                }
                (new Thread(new Runnable() {
                    @Override
                    public void run() {
                        DConnectMessage message = new DConnectResponseMessage(DConnectMessage.RESULT_ERROR);
                        URIBuilder uriBuilder = new URIBuilder();
                        uriBuilder.setProfile(ServiceInformationProfileConstants.PROFILE_NAME);
                        uriBuilder.setScheme("http");
                        uriBuilder.setHost("localhost");
                        uriBuilder.setPort(4035);
                        uriBuilder.addParameter(DConnectMessage.EXTRA_SERVICE_ID,
                                devices.get(0).getId());
                        uriBuilder.addParameter(DConnectMessage.EXTRA_ACCESS_TOKEN, mAccessToken);
                        try {
                            HttpUriRequest req = new HttpGet(uriBuilder.build());
                            req.addHeader("origin", getPackageName());
                            HttpClient client = new DefaultHttpClient();
                            HttpResponse res = client.execute(req);
                            final String result = EntityUtils.toString(res.getEntity(), "UTF-8");
                            runOnUiThread(new Runnable() {
                                public void run() {
                                    mEditText1.setText(result);
                                } });
                        } catch (IOException e) {
                            e.printStackTrace();
                        } catch (URISyntaxException e) {
                            e.printStackTrace();
                        }
                        int result = message.getInt(DConnectMessage.EXTRA_RESULT);
                        if (result == DConnectMessage.RESULT_ERROR) {
                            return;
                        }
                    }
                })).start();
                break;
        }
    }
    /**
     * Local OAuth のリスナー. */
    private AuthProcesser.AuthorizationHandler mAuthHandler
            = new AuthProcesser.AuthorizationHandler() {

        @Override
        public void onAuthorized(final String clientId, 
                                 final String accessToken) {
            mClientId = clientId;
            mAccessToken = accessToken;
            mError = null;
            MainActivity.this.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    final String oauthMessage = "Success. AccessToken="
                            + mAccessToken + " ClientId=" + clientId;
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(MainActivity.this, oauthMessage, Toast.LENGTH_LONG).show();
                            mEditText1.setText(oauthMessage);
                        }
                    });
            } });
        }
        @Override
        public void onAuthFailed(final ErrorCode error) {
            mError = error;
            mClientId = null;
            mAccessToken = null;
            MainActivity.this.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(MainActivity.this, error.toString(),
                            Toast.LENGTH_LONG).show();
                }
            });
        }
    };
}

ここまでの作成を終えた後、このサンプルをインストールして起動すると、以下のような画面が表示されます。

「Get AccessToken」Button を押下し、認証を行い、AccessTokenを取得する。

「Network Service Discovery」Buttonを押下し、デバイス一覧を取得します。

「Device」Buttonを押下し、デバイスの情報を取得します。この例では、サンプルのデバイスプラグインのデバイス情報が表示されます。

5. Appendix

5.1. ライブラリの自動インポート

Android Studioでは、必要なライブラリは、自動でインポートを行ってくれる機能があります。 それを使用することによって、インポート文を入力する手間が省くことができます。

まずは、AndroidStudioの[タイトル]>[Preferences]を選択します。

[Editor]>[Auto Import]の[Optimize imports on the fly]にチェックを入れることで、ソースコードで必要なインポート文を自動でインポートしてくれるようになります。

Clone this wiki locally
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.