Help us understand the problem. What is going on with this article?

【THETAプラグイン】THETA から手軽に Slack にアップロード

【THETAプラグイン】THETA から手軽に Slack にアップロード

はじめに

はじめまして、リコーの@yomura_です。

RICOH THETA で撮った写真をもっと気軽に共有できたら良いなと思い、
THETA Vで撮った写真をその場で Slack にアップロードする機能を実装してみました。

Slackはメッセージ送信などに関する多くのAPIを公開しています。
今回は画像ファイルをアップロードすることが目的なので、
こちらのfiles.uploadのAPIを叩いてみます。

画像ファイルがアップロードされるとSlackbotが画像を指定のチャネルに投稿してくれます。

system.png

RICOH THETAプラグインについて

THETAプラグインをご存じない方はこちらをご覧ください。
興味を持たれた方はTwitterのフォローと THETAプラグイン開発コミュニティ(Slack)への参加もよろしくお願いします。

Slackに投稿するまでの流れ

事前準備が必要ですが、3ステップで簡単に投稿できます。

  1. 無線ボタンを押してCLモードにする (※1)
  2. MODEボタンを長押ししてプラグインを起動する
  3. シャッターボタンを押す → 指定のチャンネルに画像を投稿!

※1 : CLモードの設定についてはマニュアルや動画で詳しく解説されています。

実装について

公式の SDK が提供されているので、こちらをベースにして Slack API を叩く部分だけ追加しました。
GitHub - ricohapi/theta-plugin-sdk: RICOH THETA Plug-in SDK

このプラグインのサンプルソースコードは GitHub で公開しています。

リクエストの形式

今回は HTTPクライアントライブラリ OkHttp3 を利用してPOSTリクエストを送信しました。

app の build.gradle に一行追加してビルドすると利用できるようになります。(執筆時点での最新バージョン 3.13.1)

build.gradle
dependencies {
    ...
    ...
    implementation 'com.squareup.okhttp3:okhttp:3.13.1'
}

リクエストの形式は multipart/form-data で送信しました。
multipart/form-data 形式のリクエストは OkHttp の MultipartBody クラスを使います。
送信データの境界となる文字列は現在時刻から生成しました。

final String boundary = String.valueOf(System.currentTimeMillis());

RequestBody requestBody = new MultipartBody.Builder(boundary)
                              .setType(MultipartBody.FORM)
                              ....

リクエストの引数

今回は以下の引数にデータを入れました。

引数名 説明
file 画像ファイル
token 画像を投稿してもらうSlackbotのトークン
channels Slackのチャネル名
filename 画像のファイル名
filetype 画像のデータ形式
initial_comment Slackbotが投稿する時のコメント
title 画像のタイトル

ファイルの取得

THETAで撮影した画像は外部ストレージに保存されているので、以下のようにDCIMのディレクトリを取得する必要があります。

final String dcim = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getPath();
File file = new File(dcim + fileName);

チャネル名の取得

Slackアプリでチャネル名を右クリックして「リンクを取得」すると以下のような文字列がコピーされます。この messages/ 以降の文字列がチャネル名です。

https://{ワークスペース名}.slack.com/messages/{チャネル名}

一連の流れ

まとめるとリクエスト送信部分の処理はこのようになりました。

UploadTask.java
private String postToSlackbot() {

    //  SlackAPIのURL
    final String apiUrl = "https://slack.com/api/files.upload";
    // Slackの投稿先チャネル
    final String slackChannel = "write your channel ID here"
    // Slackbotのトークン
    final String slackBotToken = "write your slackbot token here"

    // ファイル取得
    final String dcim = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getPath();
    File file = new File(dcim + this.fileName);

    // HTTPクライアント
    OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .writeTimeout(60, TimeUnit.SECONDS)
            .readTimeout(60, TimeUnit.SECONDS)
            .build();

    // リクエストボディ
    final String timestamp = getDateString();
    final String boundary = String.valueOf(System.currentTimeMillis());
    RequestBody requestBody = new MultipartBody.Builder(boundary)
            .setType(MultipartBody.FORM)
            .addFormDataPart(
                    "file",
                    fileName,
                    RequestBody.create(MediaType.parse("image/jpeg"), file)
            )
            .addFormDataPart("token", slackBotToken)
            .addFormDataPart("channels", slackChannel)
            .addFormDataPart("filename", "IMG_" + timestamp + ".jpg")
            .addFormDataPart("filetype", "jpg")
            .addFormDataPart("initial_comment", "THETA V SlackUploaderから画像がアップロードされました。")
            .addFormDataPart("title", "IMG_" + timestamp + ".jpg")
            .build();

    // リクエスト
    Request request = new Request.Builder()
            .url(apiUrl)
            .post(requestBody)
            .build();

    Call call = client.newCall(request);
    String result = null;

    try {
        // レスポンス取得
        Response response = call.execute();
        ResponseBody body = response.body();
        if (body != null) {
            result = body.string();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    return result;
}

あとは SDK にあらかじめ用意されている TakePictureTask.java を参考に、非同期処理のためのタスククラスを作り、先ほどの処理をdoInBackgroundから呼び出します。

UploadTask.java
@Override
protected String doInBackground(Void... params) {
    String result = postToSlackbot();
    return result;
}

ボタンへの割り当て

  • 今回はカメラボタンを「撮影 & Slackにアップロード」、モード切替ボタンを「チャネル切り替え」に割り当てました。
MainActivity.java
@Override
public void onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyReceiver.KEYCODE_CAMERA) {
        new TakePictureTask(mTakePictureTaskCallback).execute();  // 撮影&アップロード
    } else if (keyCode == KeyReceiver.KEYCODE_MEDIA_RECORD) {
        changeDestination();  // チャネル切り替え
    }
}

切り替えたときに今の投稿先がどのチャネルかわかるように音声読み上げの機能もつけようと試みましたが、THETA Vに内蔵のText-to-speechエンジンに日本語を喋らせることができなかったので断念しました。

THETAをしゃべらせる方法ついてはこちらの記事に詳しく紹介されています。
THETA をおしゃべりにする - Qiita

撮影ボタンを押すとこのように画像をアップロードすることができました。(画像はダミーです)

upload.png

おわりに

まだまだ荒削りですが、なんとかSlackに画像を投稿することができました。

Slackをプライベートで使っている方はもちろん、
仕事で使っているチームの方も、THETAで写真を撮って簡単なメモ代わりとして共有するという使い方もありかもしれません。

今はSlackbotのトークンや投稿先はハードコーディングされているので、
Web UIから設定できるようにできればと思います。

WebUIについての記事はこちらに詳細に書かれています。

THETAプラグインのWeb UIの実装方法【THETA プラグイン開発】 - Qiita

theta-plugin
株式会社リコーの技術者有志による、RICOH THETAプラグイン技術情報の提供を目的としたコミュニティです。
https://api.ricoh/products/theta-plugin/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした