THETA Plug-ins - Uploading to Slack Easily From THETA
THETA Plug-ins - Uploading to Slack Easily From THETA
Original post (in Japanese)
Introduction
Hello, I am Yohei Omura (@yomura_) and I work at RICOH.
I wish I could share pictures taken with RICOH THETA more easily, so I implemented a function to quickly upload photos taken with a THETA V to Slack.
Slack exposes many APIs for sending messages and other actions. My purpose is to upload photos, so I used the files.upload API.
Once a photo is uploaded, Slackbot will post it to a specified channel.
About RICOH THETA Plug-ins
If you are unfamiliar with THETA Plug-ins, please see here.
If you are interested, please join the theta360.guide developer community.
Steps Up Until Post to Slack
You need to prepare in advance, but you can easily post to Slack in 3 steps.
- Press the Wi-Fi button to switch to CL mode
- Start up the plug-in by pressing and holding the mode button
- Press the shutter button → Post the photo to the specified channel!
CL mode settings are explained in detail in manuals and videos:
Manual: https://support.theta360.com/en/manual/v/content/prepare/prepare_08.html
Video: https://www.youtube.com/watch?v=tkqyBNOWWIY&feature=youtu.be
Implementation
Since the official SDK is available, I only added the part that connects to the Slack API.
GitHub - ricohapi/theta-plugin-sdk: RICOH THETA Plug-in SDK
Sample source code for this plugin is available on GitHub.
Request Format
I used the HTTP client library OkHttp3 to send a POST request.
You can use it by adding a line to the build.gradle of the app and building it. (Latest version 3.13.1 at the time of writing.)
dependencies {
...
...
implementation 'com.squareup.okhttp3:okhttp:3.13.1'
}
The format of the request was sent in multipart / form-data. Requests in the form of multipart / form-data use OkHttp’s MultipartBody class. The character string that is the boundary of the transmitted data was generated from the current time.
final String boundary = String.valueOf(System.currentTimeMillis());
RequestBody requestBody = new MultipartBody.Builder(boundary)
.setType(MultipartBody.FORM)
....
Request Arguments
I put the data in the following arguments:
Argument Name | Description |
file | Image file |
token | The Slackbot token used to have an image posted |
channels | Slack channel name |
filename | Image filename |
filetype | Image data format |
initial_command | Comments when posted by Slackbot |
title | Image title |
Get File
Images taken with THETA are saved in external storage, so you need to get the DCIM directory as shown below.
final String dcim = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getPath();
File file = new File(dcim + fileName);
Get Filename
Right-click on the channel name in the Slack app and select “Get Link” to copy the following string. The string after this message is the channel name.
https://{workspace name}.slack.com/messages/{channel name}
A Series of Flows
The sending part of processing of a request is like this.
private String postToSlackbot() {
// SlackAPI URL
final String apiUrl = "https://slack.com/api/files.upload";
// The Slack channel you're posting to
final String slackChannel = "write your channel ID here"
// Slackbot token
final String slackBotToken = "write your slackbot token here"
// get file
final String dcim = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getPath();
File file = new File(dcim + this.fileName);
// HTTP client
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.build();
// request body
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", "Image uploaded from THETA V SlackUploader.")
.addFormDataPart("title", "IMG_" + timestamp + ".jpg")
.build();
// Request
Request request = new Request.Builder()
.url(apiUrl)
.post(requestBody)
.build();
Call call = client.newCall(request);
String result = null;
try {
// Get response
Response response = call.execute();
ResponseBody body = response.body();
if (body != null) {
result = body.string();
}
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
After that, refer to TakePictureTask.java prepared in the SDK in advance, create a task class for asynchronous processing, and call the previous processing from doInBackground.
@Override
protected String doInBackground(Void... params) {
String result = postToSlackbot();
return result;
}
Assign to Button
I assigned the camera button to “shoot & upload to Slack” and the mode switching button to “channel switching.”
@Override
public void onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyReceiver.KEYCODE_CAMERA) {
new TakePictureTask(mTakePictureTaskCallback).execute(); // take picture & upload
} else if (keyCode == KeyReceiver.KEYCODE_MEDIA_RECORD) {
changeDestination(); // change channel
}
}
I tried to add a voice reading function so that I could tell which channel the current posting destination was when I switched Slack channels. But I could not make THETA V’s built-in Text-to-Speech engine speak Japanese. So I gave up.
You can read more about how to make THETA speak in this article: Making the THETA talk (article is in Japanese)
When the shutter button is pressed, the photo can be uploaded like here. (Using a fake image here.)
Conclusion
This is still at a rough stage, but images can be posted to Slack directly from your THETA!
Whether using Slack privately or as a team at work, photos taken with THETA can be shared as an easy substitute for a memo.
Right now, the Slackbot token and destination are hard-coded, so it would be nice to be able to set it from a WebUI.
The article below shows details about the WebUI.