-
Notifications
You must be signed in to change notification settings - Fork 22
Application Manual Kotlin for Android Studio 100
- 1. はじめに
- 2. Device Connect について
- 3. サンプルアプリケーションの作成
本ドキュメントは、Device Connect APIを用いてAndroidネイティブアプリケーションを開発する手法について解説します。Androidに関する一般的な知識と、Javaに関する一般的な知識を保持している事を前提に解説していきます。
本ドキュメントでは、開発環境としてAndroid Studioを使用します。
Android Studioは、こちらからダウンロードを行ってください。
Device Connectでは、RESTfulで、命令やデータの送受信が可能です。各処理は、Profileという形でURIが割り振られ、そのURIをJavaやJavaScriptなどで呼び出す事で、処理を行います。
Device Connect で提供される Profile は、DeviceConnect-Spec で定義しています。
上記のサイトとは別にデバイス毎に固有の Profile が定義されることもあります。その場合には、Service Information などの Profile で情報を取得して使用します。
定義されている一部のプロファイルを以下に示します。
Profile名 | 説明 |
---|---|
Battery | バッテリーの状態を保持します |
DeviceOrientation | デバイスの加速度等を取得します。 |
File | デバイスとFileの送信、受信等を行います。 |
Media Player | メディアの再生(音声, 音楽, 動画)を行います。 |
MediaStream Recording | メディアの録音(音声,音楽,動画)を行います。 |
Notification | デバイスに通知(Notification)を行います。 |
Phone | デバイスに電話発信の命令を送ります。 |
Proximity | デバイスと物体との近接状態を取得します。 |
Service Discovery | デバイスの対応Profile一覧を取得します。 |
Setting | デバイスの設定(音量, 画面輝度, 画面スリープ時間等)を行います。 |
Vibration | デバイスをバイブレートする命令を送ります。 |
ただし、デバイスによって、使用出来る Profile は変わりますので、ご注意ください。
Android版Device Connectでは、端末内のローカルに起動しているHTTPサーバ(Device Connect Manager)に問い合わせを行う事で、ハードウェアの操作や、情報取得を行う事が可能です。
図1. Device Connect Managerへの問い合わせ
githubのDownload ZIPボタンを押下して、ダウンロードしてください。
もしくは、gitコマンドを用いてプロジェクトをチェックアウトしてください。
$ git clone https://github.com/DeviceConnect/DeviceConnect-Android.git
Android Studio で /DeviceConnect-Android/dConnectManager/dConnectManager にあるプロジェクトを開きます。
dconnect-manager-appを実行することで、Device Connect Manager がインストールされます。
Device Connect Managerのアイコンを押下して、アプリを起動します。
Device Connect Managerのスイッチを押下して、オンにすることでサーバを起動します。
Device Connect Manager の起動を行うとサービスのリストの中にHOSTが存在することを確認します。
以上で、Device Connectを使用するための準備が整いました。
次からサンプルの作成を説明します。
サンプルアプリケーションでは、Device Connect Managerに問い合わせを行い、結果のJSONを取得するサンプルを作成しながら解説をします。
Android Studio の[Start a new Android Studio project]を選択します。
Application Name, Project locationを入力します。
Project locationはサンプルアプリケーションのソースコードを格納するパスを任意に指定してください。
項目 | 設定値 |
---|---|
Application Name | ExmapleApp |
Project location | プロジェクトのパス(任意) |
Package Name | org.deviceconnect.android.exampleapp |
Empty Activityを選択します。
Application Name,Package Nameなどを入力して下さい。
今回はKotlinを使用するため、LanguageをKotlinにしてください。
Finish ボタンを押下して、プロジェクトを作成します。
ビルドには、GitHubアカウントとトークンが必要になります。 以下の手順により、プロジェクトでGitHubアカウントとトークンの設定を行ってください。
- GitHubの[Settings] > [Developer settings] > [Personal access tokens] から作成します。
- scopeは
repo
、write:packages
、read:packages
にチェックを付けて生成してください。 - プロジェクトのルートディレクトリに
github.properties
というファイルを作成して配置してください。 - github.propertiesにユーザ名とトークンを入力してください。
gpr.usr={ GitHubユーザ名 }
gpr.key={ トークン }
以下のように環境変数を設定することで、github.propertiesの追加を省略することができます。
$ export GPR_USER={ GitHubユーザ名 }
$ export GPR_API_KEY={ トークン }
build.gradleに以下の記述をすることで、Device Connect SDK for Androidをインポートします。
android {
...
repositories {
maven {
name = "dConnectSDKForAndroid"
/** Configure path of your package repository on Github
* Replace GITHUB_USERID with your/organisation Github userID and REPOSITORY with the repository name on GitHub
*/
url = uri("https://maven.pkg.github.com/DeviceConnect/DeviceConnect-Android")
credentials {
/**Create github.properties in root project folder file with gpr.usr=GITHUB_USER_ID & gpr.key=PERSONAL_ACCESS_TOKEN**/
username = githubProperties['gpr.usr'] ?: System.getenv("GPR_USER")
password = githubProperties['gpr.key'] ?: System.getenv("GPR_API_KEY")
}
}
}
}
dependencies {
implementation 'org.deviceconnect:dconnect-sdk-for-android:2.3.2'
}
Device Connect Managerを使用する前に、生存確認を行います。
Device Connect Managerの生存確認には以下のメソッドを使用します。
DConnectSDK#availability()
生存確認を行うためのボタンとログを表示するためのTextViewをUIに追加します。
res/layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
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"
android:orientation="vertical"
tools:context="org.deviceconnect.android.exampleapp.MainActivity">
<Button
android:id="@+id/btnAvailability"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="availability"/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/text_view_log"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</ScrollView>
</LinearLayout>
MainActivity.kt
package org.deviceconnect.android.exampleapp;
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
import org.deviceconnect.message.*
import org.deviceconnect.message.DConnectSDK.*
import org.deviceconnect.message.entity.BinaryEntity
import org.deviceconnect.message.entity.MultipartEntity
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.io.InputStream
class MainActivity : AppCompatActivity() {
private val sdk: DConnectSDK by lazy { DConnectSDKFactory.create(applicationContext, DConnectSDKFactory.Type.HTTP) }
private var serviceId: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btnAvailability.setOnClickListener {
sdk.availability {
if (it.result == DConnectMessage.RESULT_OK) {
log("Device Connect Manager is available.")
} else {
log("Device Connect Manager is unavailable.")
}
}
}
}
private fun log(text: String) {
runOnUiThread {
val log = textViewLog.text.toString() + "\n" + text
textViewLog.text = log
}
}
}
サンプルアプリケーションを実行すると以下のような画面が表示されます。
AVAILABILITYボタンを押下することでDevice Connect Managerの生存確認が行えます。
Device Connect Managerが起動している場合には、上記のようなログが表示されます。
もし、Managerが起動していない場合には、以下のAPIで起動します。
DConnectSDK#startManager(context)
- context: コンテキスト
sdk.startManager(context);
このメソッドは、URIスキームを用いてDevice Connect Managerを起動します。
同じ端末にインストールされているDevice Connect Manager以外は起動できません。
Device Connect Managerを使用するためには、Managerから認可を行い、アクセストークンを取得する必要があります。
アクセストークンの取得には以下のメソッドを使用します。
DConnectSDK#authorization(appName, scopes)
- appName: アプリケーション名を指定します。
- scopes: 使用するプロファイルの一覧を指定します。
アプリケーション認可を実行するためのボタンを追加します。
res/layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
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"
android:orientation="vertical"
tools:context="org.deviceconnect.android.exampleapp.MainActivity">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/btnAvailability"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="availability"/>
<Button
android:id="@+id/btnAuthorization"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="authorization"/>
</LinearLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/text_view_log"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</ScrollView>
</LinearLayout>
追加したボタンにアプリケーション認可を行う処理を追加します。
このサンプルアプリケーションでは、サービス一覧の取得・ライトの操作・加速度センサーの操作を行いますので、アプリケーション認可に渡すスコープには、以下の Profile を指定します。
- serviceDiscovery
- serviceInformation
- light
- deviceOrientation
MainActivity.java
btnAuthorization.setOnClickListener {
val appName: String? = "ExampleApp"
val scopes:Array<String?> = arrayOf("serviceDiscovery", "serviceInformation", "light", "deviceOrientation", "canvas")
sdk.authorization(appName, scopes, object: OnAuthorizationListener {
override fun onResponse(clientid: String?, accessToken: String?) {
// SDKにアクセストークンを設定
sdk.accessToken = accessToken
log("AccessToken=" + accessToken)
}
override fun onError(errorCode: Int, errorMessage: String?) {
log("Failed to authorization. message=" + errorMessage)
}
})
}
DConnectSDK#authorization(appName, scopes, listener) を呼び出すと以下のようなダイアログが表示されます。
ここで、サンプルアプリケーションが使用する Profile の確認を行い、ユーザから使用許可の認可を受けます。
認可された場合には、以下のようにアクセストークンが取得できます。
取得したアクセストークンは、ストレージなどに保存しておき、DConnectSDKを作り直した時に、DConnectSDKに設定します。
DConnectSDKでは、設定されたアクセストークンで Device Connect Managerにアクセスします。
不正なアクセストークンでAPIにアクセスした場合には、以下のようなエラーが発生します。
val response = sdk.serviceDiscovery()
if (response.result != DConnectMessage.RESULT_OK) {
val errorCode = ErrorCode.getInstance(response.getInt("errorCode"))
when (errorCode) {
ErrorCode.AUTHORIZATION -> {
// 認可エラー
}
ErrorCode.EXPIRED_ACCESS_TOKEN -> {
// 有効期限切れ
}
ErrorCode.EMPTY_ACCESS_TOKEN -> {
// アクセストークンがない
}
ErrorCode.SCOPE -> {
// スコープにないプロファイルにアクセスされた
}
ErrorCode.NOT_FOUND_CLIENT_ID -> {
// クライアントIDが見つからない
}
}
}
これらのエラーが発生した場合には、アクセストークンを取得し直す必要があります。
Device Connect Managerに接続されているサービス(デバイス)の一覧を取得します。
サービス一覧の取得には、以下のメソッドを使用します。
DConnectSDK#serviceDiscovery()
サービス一覧を取得を実行するためのボタンを追加します。
res/layout/activity_main.xml
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btnSeviceDiscovery"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="serviceDiscovery"/>
</LinearLayout>
MainActivity.kt
btnSeviceDiscovery.setOnClickListener {
sdk.serviceDiscovery {
if (it.result == DConnectMessage.RESULT_OK) {
for (obj in it.getList("services")) {
val service = obj as DConnectMessage
log("----")
log("serviceId: " + service.getString("id"))
log("name: " + service.getString("name"))
if (service.getString("name").equals("Host")) {
serviceId = service.getString("id")
}
}
} else {
log("Failed to get a services. message=" + it.getString("errorMessage"))
}
}
}
SERVICEDISCOVERYのボタンを押下することで、Device Connect Managerに接続されているサービスの一覧を取得することができます。
プラグインをインストールしていない状態では、HOSTのみが表示されます。
HOSTに搭載されているライトの操作を行う手順を説明します。
ライトを操作するために、点灯・消灯を行うボタンを追加します。
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btnLightOn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Light On"/>
<Button
android:id="@+id/btnLightOff"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Light Off"/>
</LinearLayout>
HOSTのライトを点灯します。
ここでは、lightIdを省略していますので、デフォルトのライトが点灯します。
ライト点灯には以下のメソッドを使用します。
DConnectSDK#post(uri, entity)
- uri: Device Connect ManagerへのリクエストURIを指定します。
- entity: POSTのBodyに送るエンティティを指定します。不要な場合にはnullを指定します。
MainActivity.kt
btnLightOn.setOnClickListener {
val builder = sdk.createURIBuilder()
builder.profile = "light"
builder.serviceId = serviceId
sdk.post(builder.toString(), null) {
if (it.result == DConnectMessage.RESULT_OK) {
log("Success.")
} else {
log("Failed to turn on a light. message=" + it.getString("errorMessage"))
}
}
}
LIGHT ONボタンを押下することで、Android端末のカメラについているライトが点灯します。
Android端末のライトは、カメラに付随していますので、他のカメラアプリなどとは同時に使用することができませんので、ご注意ください。
HOSTのライトを消灯します。
ライト消灯には以下のメソッドを使用します。
DConnectSDK#delete(uri)
- uri: Device Connect ManagerへのリクエストURIを指定します。
MainActivity.kt
btnLightOff.setOnClickListener {
val builder = sdk.createURIBuilder()
builder.profile = "light"
builder.serviceId = serviceId
sdk.delete(builder.toString()) {
if (it.result == DConnectMessage.RESULT_OK) {
log("Success.")
} else {
log("Failed to turn on a light. message=" + it.getString("errorMessage"))
}
}
}
LIGHT OFFボタンを押下することで、点灯していたライトが消灯します。
HOSTから加速度センサーのイベント受信するためには、WebSocketでDevice Connect Managerと接続する必要があります。
WebSocketを接続するには以下のメソッドを使用します。
DConnectSDK#connectWebSocket(listener)
- listener: WebSocketの接続・切断などのイベントを受け取るリスナーを指定します。
WebSocketの接続・切断を行うためのボタンを追加します。
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btnWebsocketConnect"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Connect"/>
<Button
android:id="@+id/btnWebsocketDisconnect"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Disconnect"/>
</LinearLayout>
MainActivity.kt
btnWebsocketConnect.setOnClickListener {
sdk.connectWebSocket(object : OnWebSocketListener {
override fun onOpen() {
log("Websocket is open.")
}
override fun onClose() {
log("Websocket is close.")
}
override fun onError(e: Exception) {
log("Websocket is error. e=" + e.message)
}
})
}
イベント開始後にWebSocketが切断された場合には、同じオリジンで登録されているイベントが全て解除されます。
WebSocketが切断された場合には、再開する場合には再度イベントを登録する必要があります。
オリジンは、デフォルトではアプリのパッケージ名が指定されています。
必要に応じて、DConnectSDK#setOrigin(origin)で変更することができます。
WebSocketを接続した際には、最後に必ずWebSocketを切断する必要があります。
WebSocketを切断するには以下のメソッドを使用します。
DConnectSDK#disconnectWebSocket()
MainActivity.kt
btnWebsocketDisconnect.setOnClickListener {
sdk.disconnectWebSocket();
}
WebSocketを切断せずに終了した場合にはメモリリークになる恐れがありますので、ご注意ください。
加速度センサーを受信するには加速度センサーの開始を行う必要があります。
イベントの開始には以下のメソッドを使用します。
DConnectSDK.addEventListener(uri, listener)
- uri: 開始するイベントへのURI
- listener: イベントの開始のレスポンス、イベントの受信を行うリスナー。
加速度センサーの開始・停止を行うボタンを追加します。
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btnSensorOn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Sensor On"/>
<Button
android:id="@+id/btnSensorOff"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Sensor Off"/>
</LinearLayout>
MainActivity.kt
btnSensorOn.setOnClickListener {
val builder = sdk.createURIBuilder()
builder.profile = "deviceOrientation"
builder.attribute = "onDeviceOrientation"
builder.serviceId = serviceId
sdk.addEventListener(builder.toString(), object : OnEventListener {
override fun onMessage(message: DConnectEventMessage) {
log("Event: $message")
}
override fun onResponse(response: DConnectResponseMessage) {
if (response.result == DConnectMessage.RESULT_OK) {
log("Success to add event.")
} else {
log("Failed to add event. message=" + response.getString("errorMessage"))
}
}
})
}
イベントが開始されると画面にログが表示されます。
イベントの停止には以下のメソッドを使用します。
DConnectSDK.removeEventListener(uri)
- uri: 停止するイベントへのURI
MainActivity.kt
btnSensorOff.setOnClickListener {
val builder = sdk.createURIBuilder()
builder.profile = "deviceOrientation"
builder.attribute = "onDeviceOrientation"
builder.serviceId = serviceId
sdk.removeEventListener(builder.toString())
}
DConnectSDK#post(uri, entity)でデータを送信する際の第2引数のentityには以下のクラスが指定できます。
- StringEntity
- BinaryEntity
- MultipartEntity
文字列のデータをPOSTのBodyに指定できます。
val entity = StringEntity("key=value")
val response: DConnectResponseMessage = sdk.post(uri, entity)
バイト配列のデータをPOSTのBodyに指定できます。
val byte = {...} as ByteArray?
entity.add("data", BinaryEntity(byte))
val response: DConnectResponseMessage = sdk.post(uri, entity)
マルチパートのデータをPOSTのBodyに指定できます。
val data = MultipartEntity()
data.add("key", StringEntity("value"))
data.add("data", FileEntity(File(path)))
val response: DConnectResponseMessage = sdk.post(uri, entity)
bodyを空で送りたい場合には、entityにnullを指定します。
btnCanvas.setOnClickListener {
val builder = sdk.createURIBuilder()
builder.profile = "canvas"
builder.attribute = "drawImage"
builder.serviceId = serviceId;
val entity = MultipartEntity();
entity.add("data", BinaryEntity(getImage()));
sdk.post(builder.toString(), entity){
if (it.result == DConnectMessage.RESULT_OK) {
log("Success to send image.")
} else {
log("Failed to send image. message=" + it.getString("errorMessage"))
}
}
}
assetsフォルダから画像のバイナリデータを取得します。
private fun getImage(): ByteArray? {
val out = ByteArrayOutputStream()
val buf = ByteArray(1024)
try {
var len = 0
val `in`: InputStream = assets.open("ic_launcher.png")
while (`in`.read(buf).also({ len = it }) > 0) {
out.write(buf, 0, len)
}
`in`.close()
} catch (e: IOException) {
// ignore.
}
return out.toByteArray()
}